class Kitchen::Driver::Vz

Virtuozzo driver for Kitchen.

@author Pavel Yudin <pyudin@parallels.com>

Public Instance Methods

create(state) click to toggle source
# File lib/kitchen/driver/vz.rb, line 44
def create(state)
  return if state[:ct_id]
  state[:ct_id] = SecureRandom.uuid
  generate_keys
  state[:ssh_key] = config[:ssh_key] = config[:private_key]
  create_ct(state)
  set_ct_network(state)
  set_ct_cpu(state)
  set_ct_mem(state)
  set_ct_disk(state)
  set_additional_options(state)
  run_ct(state)
  create_user(state)
  state[:hostname] = ct_ip(state)
  wait_for_sshd(state[:hostname], nil, keys: [state[:ssh_key]])
end
destroy(state) click to toggle source
# File lib/kitchen/driver/vz.rb, line 61
def destroy(state)
  execute_command("#{prlctl} stop #{state[:ct_id]}") if state[:ct_id]
  execute_command("#{prlctl} destroy #{state[:ct_id]}") if state[:ct_id]
end

Private Instance Methods

create_ct(state) click to toggle source
# File lib/kitchen/driver/vz.rb, line 92
def create_ct(state)
  command_line = "#{vzctl} create #{state[:ct_id]} --hostname #{config[:ct_hostname]} --ostemplate "
  command_line += config[:ostemplate] || "#{platform_major}-#{config[:arch]}"
  execute_command(command_line)
end
create_user(state) click to toggle source
# File lib/kitchen/driver/vz.rb, line 136
def create_user(state)
  ["useradd #{config[:username]}",
   "mkdir -p /home/#{config[:username]}/.ssh",
   "chown #{config[:username]}: /home/#{config[:username]}",
   "chmod 700 /home/#{config[:username]}",
   "chown #{config[:username]}: /home/#{config[:username]}/.ssh",
   "chmod 700 /home/#{config[:username]}/.ssh",
   "echo '#{File.open(config[:public_key]).read}' > /home/#{config[:username]}/.ssh/authorized_keys",
   "chown #{config[:username]}: /home/#{config[:username]}/.ssh/authorized_keys",
   "chmod 600 /home/#{config[:username]}/.ssh/authorized_keys",
   'mkdir -p /etc/sudoers.d',
   "echo '#includedir /etc/sudoers.d' >> /etc/sudoers",
   "echo '#{config[:username]} ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers.d/#{config[:username]}",
   "chmod 0440 /etc/sudoers.d/#{config[:username]}"].each do |command|
     execute_command("#{prlctl} exec #{state[:ct_id]} \"#{command}\"")
   end
end
ct_ip(state) click to toggle source
# File lib/kitchen/driver/vz.rb, line 154
def ct_ip(state)
  ip = nil
  30.times do
    output = execute_command("#{vzctl} exec #{state[:ct_id]} \"/sbin/ip -o -f inet addr show dev eth0\"")
    result = %r{(([0-9]{1,3}\.){3}[0-9]{1,3})\/[0-9]{1,2}}.match(output)
    ip = result[1] if result
    break if ip
    sleep(1)
  end
  raise "Can't detect an IP!" unless ip
  ip
end
execute_command(command) click to toggle source
# File lib/kitchen/driver/vz.rb, line 171
def execute_command(command)
  if config[:socket] == 'local'
    run_command(command)
  else
    command = 'sudo -E ' + command if config[:use_sudo]
    channel = ssh_connection.open_channel do |ch|
      ch.exec(command) do |_ch, _success|
        channel[:data] = ''
        channel[:ext_data] = ''

        channel.on_data do |_ch, data|
          channel[:data] << data
        end

        channel.on_extended_data do |_ch, _type, data|
          channel[:ext_data] << data
        end

        channel.on_request 'exit-status' do |_ch, data|
          if data.read_long.to_i != 0
            raise "SSH command failed with: #{channel[:ext_data]}"
          else
            puts channel[:data] unless channel[:data].empty?
          end
        end
      end
      ch.wait
    end
    channel.wait
    channel[:data]
  end
end
generate_keys() click to toggle source
# File lib/kitchen/driver/vz.rb, line 76
def generate_keys
  if !File.exist?(config[:public_key]) || !File.exist?(config[:private_key])
    private_key = OpenSSL::PKey::RSA.new(2048)
    blobbed_key = Base64.encode64(private_key.to_blob).delete("\n")
    public_key = "ssh-rsa #{blobbed_key} kitchen_key"
    File.open(config[:private_key], 'w') do |file|
      file.write(private_key)
      file.chmod(0600)
    end
    File.open(config[:public_key], 'w') do |file|
      file.write(public_key)
      file.chmod(0600)
    end
  end
end
platform_major() click to toggle source
# File lib/kitchen/driver/vz.rb, line 167
def platform_major
  instance.platform.name.split('.')[0]
end
prlctl() click to toggle source
# File lib/kitchen/driver/vz.rb, line 68
def prlctl
  '/usr/bin/prlctl'
end
run_ct(state) click to toggle source
# File lib/kitchen/driver/vz.rb, line 132
def run_ct(state)
  execute_command("#{prlctl} start #{state[:ct_id]}")
end
set_additional_options(state) click to toggle source
# File lib/kitchen/driver/vz.rb, line 126
def set_additional_options(state)
  unless config[:additional_options].empty?
    execute_command("#{prlctl} set #{state[:ct_id]} #{config[:additional_options].join(' ')}")
  end
end
set_ct_cpu(state) click to toggle source
# File lib/kitchen/driver/vz.rb, line 113
def set_ct_cpu(state)
  execute_command("#{prlctl} set #{state[:ct_id]} --cpus #{config[:customize][:cpus]}")
end
set_ct_disk(state) click to toggle source
# File lib/kitchen/driver/vz.rb, line 121
def set_ct_disk(state)
  ds = config[:customize][:disk]
  execute_command("#{vzctl} set #{state[:ct_id]} --diskspace #{ds}:#{ds} --save")
end
set_ct_mem(state) click to toggle source
# File lib/kitchen/driver/vz.rb, line 117
def set_ct_mem(state)
  execute_command("#{prlctl} set #{state[:ct_id]} --memsize #{config[:customize][:memory]}")
end
set_ct_network(state) click to toggle source
# File lib/kitchen/driver/vz.rb, line 98
def set_ct_network(state)
  iface_number = 0
  config[:network].each do |network, _settings|
    execute_command("#{vzctl} set #{state[:ct_id]} --netif_add eth#{iface_number} --save")
    command_line = "#{vzctl} set #{state[:ct_id]} --network #{network} --ifname eth#{iface_number} "
    command_line += '--dhcp yes ' if config[:network][network][:dhcp]
    command_line += "--ipadd #{config[:network][network][:ip]} " if config[:network][network][:ip]
    command_line += "--gw #{config[:network][network][:gw]} " if config[:network][network][:gw]
    command_line += '--save'
    execute_command(command_line)
    iface_number += 1
  end
  execute_command("#{prlctl} set #{state[:ct_id]} --netfilter full")
end
ssh_connection() click to toggle source
# File lib/kitchen/driver/vz.rb, line 210
def ssh_connection
  @connection ||= Net::SSH.start(uri.host, uri.user, port: uri.port, forward_agent: config[:use_agent_forwarding])
end
uri() click to toggle source
# File lib/kitchen/driver/vz.rb, line 204
def uri
  uri = URI(config[:socket])
  raise "Invalid URI scheme: #{uri.scheme}. Only 'ssh' is supported." if uri.scheme != 'ssh'
  uri
end
vzctl() click to toggle source
# File lib/kitchen/driver/vz.rb, line 72
def vzctl
  '/usr/sbin/vzctl'
end