Commit 5ac30e8c authored by Tails developers's avatar Tails developers
Browse files

Restore the background from a VM snapshots.

This will greatly speed up the test suite, and will reduce the stress
on the Tor network.
parent 9c8951c8
......@@ -2,7 +2,45 @@ require 'fileutils'
require 'date'
require 'system_timer'
def wait_until_remote_shell_is_up
try_for(120, lambda{ @vm.execute('true').success? })
end
def wait_until_tor_is_working
try_for(120, lambda{ @vm.execute(
'. /usr/local/lib/tails-shell-library/tor.sh; ' +
'tor_control_getinfo status/circuit-established',
'root').stdout == "1\n" })
end
def restore_background
snapshot = Dir.pwd + "/tmpfs/IceweaselBackgroundDone.state"
@vm.restore_snapshot(snapshot)
# Wait for virt-viewer to be available to sikuli. Otherwise we could
# lose sikuli actions (e.g. key presses) if they come really early
# after the restore
@screen.wait("IceweaselRunning.png", 10)
# The guest's Tor's circuits' states are likely to get out of sync
# with the other relays, so we ensure that we have fresh circuits.
# Time jumps and incorrect clocks also confuses Tor in many ways.
wait_until_remote_shell_is_up
@vm.execute("service tor stop", "root")
@vm.host_to_guest_time_sync
@vm.execute("service tor start", "root")
wait_until_tor_is_working
end
Given /^I restore the background snapshot if it exists$/ do
snapshot = Dir.pwd + "/tmpfs/IceweaselBackgroundDone.state"
if File.exists?(snapshot)
restore_background
@background_restored = true
end
end
Given /^a freshly started Tails$/ do
next if @background_restored
@vm.start
@screen.wait('TailsBootSplash.png', 30)
# Start the VM remote shell
......@@ -12,11 +50,14 @@ Given /^a freshly started Tails$/ do
end
Given /^the network traffic is sniffed$/ do
# The sniffer is external to the restored VM's state, so we can't skip
# it when restoring the background from a snapshot.
@sniffer = Sniffer.new("TestSniffer", @vm.net.bridge_name, @vm.ip, @vm.ip6)
@sniffer.capture
end
Given /^I log in to a new session$/ do
next if @background_restored
@screen.click('TailsGreeterLoginButton.png')
end
......@@ -37,18 +78,46 @@ def try_for(t, f)
end
Given /^I have a network connection$/ do
next if @background_restored
# Wait until the VM's remote shell is available, which implies
# that the network is up.
try_for(120, lambda{ @vm.execute('true').success? })
wait_until_remote_shell_is_up
end
Given /^Tor has bootstrapped$/ do
next if @background_restored
# FIXME: A better approach would be to check this via the control
# port with: GETINFO status/circuit-established
cmd = 'grep -q "Bootstrapped 100%" /var/log/tor/log'
try_for(120, lambda{ @vm.execute(cmd, "root").success? })
end
Given /^Iceweasel has autostarted and is not loading a web page$/ do
next if @background_restored
@screen.wait("IceweaselRunning.png", 120)
# Stop iceweasel to load its home page. We do this to prevent Tor
# from gerring confused in case we save and restore a snapshot in
# the middle of loading a page.
@screen.type("l", Sikuli::KEY_CTRL)
@screen.type("about:blank" + Sikuli::KEY_RETURN)
end
Given /^the time has synced$/ do
next if @background_restored
["/var/run/tordate/done", "/var/run/htpdate/success"].each do |file|
try_for(300, lambda{ @vm.execute("test -e #{file}").success? })
end
end
Given /^I save the background snapshot if it does not exist$/ do
if !@background_restored
snapshot = Dir.pwd + "/tmpfs/IceweaselBackgroundDone.state"
@vm.save_snapshot(snapshot)
restore_background
end
end
Then /^I see "([^"]*)" after at most (\d+) seconds$/ do |image, time|
@screen.wait(image, time.to_i)
end
......
......@@ -2,12 +2,15 @@ Feature: Iceweasel must be anonymous.
In order to be anonymous, the iceweasel web browser must connect through Tor.
Background:
Given a freshly started Tails
Given I restore the background snapshot if it exists
And a freshly started Tails
And the network traffic is sniffed
And I log in to a new session
And I have a network connection
And Tor has bootstrapped
And I see "IceweaselRunning.png" after at most 120 seconds
And Iceweasel has autostarted and is not loading a web page
And the time has synced
And I save the background snapshot if it does not exist
Scenario: Opening check.torproject.org in Iceweasel will show the green onion and the congratualtions message.
When I open the address "https://check.torproject.org" in Iceweasel
......
......@@ -33,6 +33,8 @@ class Display
end
def start_virtviewer(domain)
# virt-viewer forks, so we cannot (easily) get the child pid
# and use it in active? and stop_virtviewer below...
IO.popen(["virt-viewer", "-d",
"-f",
"-r",
......@@ -42,6 +44,10 @@ class Display
"&"].join(' '))
end
def active?
system("pkill -0 virt-viewer")
end
def stop_virtviewer
system("killall virt-viewer")
end
......
......@@ -9,7 +9,12 @@ class IPAddr
IPAddr.new("192.168.0.0/16")
]
PrivateIPv6Range = IPAddr.new("fc00::/7")
# Tails' firewall apparently leaks multicast ff02::1 == "all
# (link-local) nodes address"
PrivateIPv6Ranges = [
IPAddr.new("fc00::/7"), # private
IPAddr.new("ff02::1/64") # link-local multicast
]
def private?
if self.ipv4?
......@@ -18,7 +23,10 @@ class IPAddr
end
return false
else
return PrivateIPv6Range.include?(self)
PrivateIPv6Ranges.each do |ipr|
return true if ipr.include?(self)
end
return false
end
end
......
......@@ -12,6 +12,12 @@ class VM
@read_net_xml = File.read(net_xml)
@parsed_domain_xml = REXML::Document.new(@read_domain_xml)
@parsed_net_xml = REXML::Document.new(@read_net_xml)
@ip = @parsed_net_xml.elements['network/ip/dhcp/host/'].attributes['ip']
@parsed_net_xml.elements.each('network/ip') do |e|
if e.attribute('family').to_s == "ipv6"
@ip6 = e.attribute('address').to_s
end
end
@iso = ENV['ISO'] || get_last_iso
@virt = Libvirt::open("qemu:///system")
setup_temp_domain
......@@ -22,18 +28,16 @@ class VM
domain_name = @parsed_domain_xml.elements['domain/name'].text
begin
old_domain = @virt.lookup_domain_by_name(domain_name)
rescue Libvirt::RetrieveError
else
old_domain.destroy if old_domain.active?
old_domain.undefine
rescue
end
net_name = @parsed_net_xml.elements['network/name'].text
begin
old_net = @virt.lookup_network_by_name(net_name)
rescue Libvirt::RetrieveError
else
old_net.destroy if old_net.active?
old_net.undefine
rescue
end
end
......@@ -47,12 +51,6 @@ class VM
def setup_network
@net = @virt.define_network_xml(@read_net_xml)
@net.create
@ip = @parsed_net_xml.elements['network/ip/dhcp/host/'].attributes['ip']
@parsed_net_xml.elements.each('network/ip') do |e|
if e.attribute('family').to_s == "ipv6"
@ip6 = e.attribute('address').to_s
end
end
end
def get_last_iso
......@@ -80,6 +78,35 @@ EOF
return VMCommand.new(self, cmd, user)
end
def host_to_guest_time_sync
host_time= DateTime.now.strftime("%s").to_s
execute("date -s '@#{host_time}'", "root").success?
end
def save_snapshot(path)
@domain.save(path)
@display.stop
end
def restore_snapshot(path)
# Undefine current domain so it can be restored
@domain.destroy if @domain.active?
begin
@domain.undefine
rescue
# FIXME: why exception sometimes?
end
# FIXME: is restore broken?
# @domain.restore(path)
# workaround based on virsh
system("virsh -c qemu:///system restore --file #{path}")
@domain = @virt.lookup_domain_by_name("TailsToaster")
# /workaraound
@display = Display.new(@domain.name)
@display.start
end
def start
@domain.destroy if @domain.active?
@domain.create
......@@ -89,7 +116,12 @@ EOF
def stop
@domain.destroy if @domain.active?
@domain.undefine
begin
@domain.undefine
rescue
# FIXME: why does this happen after snapshot restore?
puts "Domain couldn't be undefined"
end
@net.destroy if @net.active?
@net.undefine
@display.stop
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment