Class: HybridPlatformsConductor::HpcPlugins::Provisioner::Proxmox

Inherits:
Provisioner
  • Object
show all
Defined in:
lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb

Overview

Provision Proxmox containers

Constant Summary collapse

MAX_PROXMOX_HOSTNAME_SIZE =

Maximum size in chars of hostnames set in Proxmox

64

Constants included from LoggerHelpers

LoggerHelpers::LEVELS_MODIFIERS, LoggerHelpers::LEVELS_TO_STDERR

Class Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Provisioner

#initialize, #wait_for_port, #wait_for_port!, #wait_for_state, #wait_for_state!, #with_running_instance

Methods included from LoggerHelpers

#err, #init_loggers, #log_component=, #log_debug?, #log_level=, #out, #section, #set_loggers_format, #stderr_device, #stderr_device=, #stderr_displayed?, #stdout_device, #stdout_device=, #stdout_displayed?, #stdouts_to_s, #with_progress_bar

Methods inherited from Plugin

extend_config_dsl_with, #initialize, valid?

Constructor Details

This class inherits a constructor from HybridPlatformsConductor::Provisioner

Class Attribute Details

.proxmox_waiter_files_mutexObject

Returns the value of attribute proxmox_waiter_files_mutex.



71
72
73
# File 'lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb', line 71

def proxmox_waiter_files_mutex
  @proxmox_waiter_files_mutex
end

Instance Method Details

#createObject

Create an instance. Reuse an existing one if it already exists.

API
  • This method is mandatory



81
82
83
84
85
86
87
88
89
90
91
92
93
94
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb', line 81

def create
  # First check if we already have a test container that corresponds to this node and environment
  @lxc_details = nil
  with_proxmox do |proxmox|
    proxmox_get(proxmox, 'nodes').each do |node_info|
      if proxmox_test_info[:test_config][:pve_nodes].include?(node_info['node']) && node_info['status'] == 'online'
        proxmox_get(proxmox, "nodes/#{node_info['node']}/lxc").each do |lxc_info|
          vm_id = Integer(lxc_info['vmid'])
          if vm_id.between?(*proxmox_test_info[:test_config][:vm_ids_range])
            # Check if the description contains our ID
            lxc_config = proxmox_get(proxmox, "nodes/#{node_info['node']}/lxc/#{vm_id}/config")
            vm_description_lines = (lxc_config['description'] || '').split("\n")
            hpc_marker_idx = vm_description_lines.index('===== HPC info =====')
            unless hpc_marker_idx.nil?
              # Get the HPC info associated to this VM
              # Hash<Symbol,String>
              vm_hpc_info = Hash[vm_description_lines[hpc_marker_idx + 1..-1].map do |line|
                property, value = line.split(': ')
                [property.to_sym, value]
              end]
              if vm_hpc_info[:node] == @node && vm_hpc_info[:environment] == @environment
                # Found it
                # Get back the IP
                ip_found = nil
                lxc_config['net0'].split(',').each do |net_info|
                  property, value = net_info.split('=')
                  if property == 'ip'
                    ip_found = value.split('/').first
                    break
                  end
                end
                raise "[ #{@node}/#{@environment} ] - Unable to get IP back from LXC container nodes/#{node_info['node']}/lxc/#{vm_id}/config" if ip_found.nil?
                @lxc_details = {
                  pve_node: node_info['node'],
                  vm_id: vm_id,
                  vm_ip: ip_found
                }
                break
              end
            end
          end
        end
        break if @lxc_details
      end
    end
  end
  unless @lxc_details
    # We couldn't find an existing LXC container for this node/environment.
    # We have to create one.
    # Get the image name for this node
    image = @nodes_handler.get_image_of(@node).to_sym
    # Find if we have such an image registered
    if @config.known_os_images.include?(image)
      proxmox_conf = "#{@config.os_image_dir(image)}/proxmox.json"
      if File.exist?(proxmox_conf)
        pve_template = JSON.parse(File.read(proxmox_conf)).dig 'template'
        if pve_template
          # Query the inventory to know about minimum resources needed to deploy the node.
          # Provide default values if they are not part of the metadata.
          min_resources_to_deploy = {
            cpus: 2,
            ram_mb: 1024,
            disk_gb: 10
          }.merge(@nodes_handler.get_deploy_resources_min_of(@node) || {})
          # Create the Proxmox container from the sync node.
          vm_config = proxmox_test_info[:vm_config]
          # Hostname in Proxmox is capped at 65 chars.
          # Make sure we don't get over it, but still use a unique one.
          hostname = "#{@node}.#{@environment}.hpc-test.com"
          if hostname.size > MAX_PROXMOX_HOSTNAME_SIZE
            # Truncate it, but add a unique ID in it.
            # In the end the hostname looks like:
            # <truncated_node_environment>.<unique_id>.hpc-test.com
            hostname = "-#{Digest::MD5.hexdigest(hostname)[0..7]}.hpc-test.com"
            hostname = "#{@node}.#{@environment}"[0..MAX_PROXMOX_HOSTNAME_SIZE - hostname.size - 1] + hostname
          end
          @lxc_details = request_lxc_creation_for({
            ostemplate: pve_template,
            hostname: hostname.gsub('_', '-'),
            cores: min_resources_to_deploy[:cpus],
            cpulimit: min_resources_to_deploy[:cpus],
            memory: min_resources_to_deploy[:ram_mb],
            rootfs: "local-lvm:#{min_resources_to_deploy[:disk_gb]}",
            nameserver: vm_config[:vm_dns_servers].join(' '),
            searchdomain: vm_config[:vm_search_domain],
            net0: "name=eth0,bridge=vmbr0,gw=#{vm_config[:vm_gateway]}",
            password: 'root_pwd',
            description: <<~EOS
              ===== HPC info =====
              node: #{@node}
              environment: #{@environment}
              debug: #{log_debug? ? 'true' : 'false'}
            EOS
          })
        else
          raise "[ #{@node}/#{@environment} ] - No template found in #{proxmox_conf}"
        end
      else
        raise "[ #{@node}/#{@environment} ] - No Proxmox configuration found at #{proxmox_conf}"
      end
    else
      raise "[ #{@node}/#{@environment} ] - Unknown OS image #{image} defined for node #{@node}"
    end
  end
end

#default_timeoutObject

Return the default timeout to apply when waiting for an instance to be started/stopped…

API
  • This method is optional

Result
  • Integer: The timeout in seconds



262
263
264
# File 'lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb', line 262

def default_timeout
  proxmox_test_info[:default_timeout] || 3600
end

#destroyObject

Destroy an instance Prerequisite: create has been called before

API
  • This method is mandatory



210
211
212
213
# File 'lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb', line 210

def destroy
  log_debug "[ #{@node}/#{@environment} ] - Delete Proxmox LXC Container ..."
  release_lxc_container(@lxc_details[:vm_id])
end

#ipObject

Return the IP address of an instance. Prerequisite: create has been called before.

API
  • This method is optional

Result
  • String or nil: The instance IP address, or nil if this information is not relevant



253
254
255
# File 'lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb', line 253

def ip
  @lxc_details[:vm_ip]
end

#startObject

Start an instance Prerequisite: create has been called before

API
  • This method is mandatory



190
191
192
193
194
195
# File 'lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb', line 190

def start
  log_debug "[ #{@node}/#{@environment} ] - Start Proxmox LXC Container ..."
  with_proxmox do |proxmox|
    run_proxmox_task(proxmox, :post, @lxc_details[:pve_node], "lxc/#{@lxc_details[:vm_id]}/status/start")
  end
end

#stateObject

Return the state of an instance

API
  • This method is mandatory

Result
  • Symbol: The state the instance is in. Possible values are:

    • :missing: The instance does not exist

    • :created: The instance has been created but is not running

    • :running: The instance is running

    • :exited: The instance has run and is now stopped

    • :error: The instance is in error



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb', line 225

def state
  if @lxc_details.nil?
    :missing
  else
    status = nil
    with_proxmox do |proxmox|
      vm_id_str = @lxc_details[:vm_id].to_s
      status =
        if proxmox_get(proxmox, "nodes/#{@lxc_details[:pve_node]}/lxc").any? { |data_info| data_info['vmid'] == vm_id_str }
          status_info = proxmox_get(proxmox, "nodes/#{@lxc_details[:pve_node]}/lxc/#{@lxc_details[:vm_id]}/status/current")
          # Careful that it is possible that somebody destroyed the VM and so its status is missing
          status = status_info.key?('status') ? status_info['status'].to_sym : :missing
          status = :exited if status == :stopped
          status
        else
          :missing
        end
    end
    status
  end
end

#stopObject

Stop an instance Prerequisite: create has been called before

API
  • This method is mandatory



200
201
202
203
204
205
# File 'lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb', line 200

def stop
  log_debug "[ #{@node}/#{@environment} ] - Stop Proxmox LXC Container ..."
  with_proxmox do |proxmox|
    run_proxmox_task(proxmox, :post, @lxc_details[:pve_node], "lxc/#{@lxc_details[:vm_id]}/status/stop")
  end
end