]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
Initial version of the ruby version of the make-testing script.
authorTobias Brunner <tobias@strongswan.org>
Fri, 12 Jun 2009 08:21:46 +0000 (10:21 +0200)
committerTobias Brunner <tobias@strongswan.org>
Tue, 15 Sep 2009 12:15:17 +0000 (14:15 +0200)
testing/config/default.yml [new file with mode: 0644]
testing/lib/config.rb [new file with mode: 0644]
testing/lib/console_format.rb [new file with mode: 0644]
testing/lib/guest_config.rb [new file with mode: 0644]
testing/lib/kernel_config.rb [new file with mode: 0644]
testing/lib/strongswan_config.rb [new file with mode: 0644]
testing/lib/testing.rb [new file with mode: 0644]
testing/make-testing.rb [new file with mode: 0755]

diff --git a/testing/config/default.yml b/testing/config/default.yml
new file mode 100644 (file)
index 0000000..fd1572b
--- /dev/null
@@ -0,0 +1,135 @@
+# Global configuration file for strongSwan UML testing.
+#
+# Copyright (C) 2009 Tobias Brunner
+# Hochschule fuer Technik Rapperswil
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# for more details.
+
+# All tarballs must have on of the following extensions:
+#   .tar, .tar.gz, .tgz, .tar.bz2, .tbz
+
+<% testdir=TESTING_ROOT %>
+
+guests:
+  defaults:
+    # the strongswan version to be used. either the name of a strongswan config
+    # defined below or an inline strongswan config.
+    strongswan: default
+#strongswan:
+#     source: git
+    # the kernel to be used. either the name of a kernel config defined below or
+    # an inline kernel config.
+    kernel: default
+    # the masterfs to be used (folder or tarball)
+    #masterfs: <%= testdir %>/master-fs-20090325.tar.bz2
+    masterfs: <%= testdir %>/master
+    # templates (config files etc.) that are copied to the guests. a directory
+    # with the guest's name must exist in the directry set here.
+    templates: <%= testdir %>/hosts
+    # memory in MB per guest
+    mem: 64
+    # consoles: xterm|pts
+    consoles:
+      - xterm
+      - pts
+  # allows to customize the values above for each host (options set here
+  # replace the defaults above for the specified host). Only the hosts listed
+  # here are actually built.
+  # possible values are: sun, moon, dave, carol, alice, venus bob and winnetou.
+  # of course, you can add other hostnames for your own scenarios.
+  hosts:
+    - sun:
+        strongswan: other
+    - moon:
+    - dave
+    - carol:
+#    kernel: older
+    - alice:
+    - venus
+    - bob:
+        kernel: already_built
+        mem: 128
+    - winnetou
+
+# list of kernels
+kernels:
+  default:
+    # which source is used to build the kernel (none|dir|git|tar)
+    # where 'dir' is synonymous with 'git' but 'checkout' is ignored
+    # if this option is omitted, 'none' is assumed
+    source: git
+    # if you set source to 'git', set the path to the git repository here
+    path: /home/tbrunner/kernel/net-next-2.6/
+    # if source is 'git', this is the local branch, tag or commit to build.
+    # a tarball is created from there (using git archive) and that tarball
+    # is then used to build the kernel.
+    # if this option is omitted or empty, the build takes place directly in
+    # the repository.
+    checkout: 'v2.6.28'
+    patches:
+      - /home/tbrunner/workspace/git/src/dumm/patches/mconsole-exec-2.6.28.patch
+    # kernel configuration file, if no config file is defined, it is assumed
+    # that the config file is available in the source directory
+    config: <%= testdir %>/.config-2.6.28
+  older:  
+    # tarball source
+    source: tar
+    path: <%= testdir %>/linux-2.6.29.4.tar.bz2
+    # list of optional kernel patches (.patch, .patch.bz2, .patch.gz extension)
+    patches:
+      #- <%= testdir %>/aead_init.patch.bz2
+    # kernel configuration file
+    config: <%= testdir %>/.config-2.6.29
+
+  already_built:
+    # if source is not defined or set to 'none' the kernel can be specified
+    # directly
+#source: none
+    path: /home/tbrunner/tbrunner/testing/umlbuild/linux-uml-2.6.21.1
+
+strongswan:
+  default:
+    # same as for the kernel (dir|git|tar) 
+    source: git
+    path: /home/tbrunner/workspace/git/
+    checkout: master
+    # list of configure options
+    # e.g. eap_aka: yes -> --enable-eap-aka
+    #   or eap_md5: no  -> --disable-eap-md5
+    options:
+      - curl: no
+      - ldap: no
+      - eap_aka: yes
+      - eap_sim: yes
+      - eap_sim_file: yes
+      - eap_md5: yes
+      - eap_mschapv2: yes
+      - eap_identity: yes
+      - eap_radius: yes
+      - sql: no
+      - sqlite: no
+      - md4: yes # required by eap-mschapv2
+      - mediation: yes
+      - openssl: yes
+      - blowfish: yes
+      - twofish: yes
+      - serpent: yes
+      - kernel_pfkey: yes
+      - integrity_test: yes
+      - leak_detective: yes
+      - load_tests: yes
+  other:
+    source: tar
+    path: <%= testdir %>/strongswan-4.3.1.tar.gz
+    options:
+      - gmp: no
+      - openssl: yes
+
diff --git a/testing/lib/config.rb b/testing/lib/config.rb
new file mode 100644 (file)
index 0000000..534d1de
--- /dev/null
@@ -0,0 +1,119 @@
+=begin
+  Copyright (C) 2009 Tobias Brunner
+  Hochschule fuer Technik Rapperswil
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by the
+  Free Software Foundation; either version 2 of the License, or (at your
+  option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+  for more details.
+=end
+
+require 'ostruct'
+
+require 'kernel_config'
+require 'strongswan_config'
+require 'guest_config'
+
+module Dumm
+  class Config
+
+    # Creates a new Config instance, initialized with the values
+    # loaded from the config file.
+    def initialize(file)
+      @config = read_config(file)
+      @kernels = prepare_kernels
+      @strongswan = prepare_strongswan
+      @guests = prepare_guests
+    end
+
+    # Return all strongSwan configurations that are referenced by any guest.
+    def strongswan
+      @strongswan.select do |name, strongswan|
+        @guests.any? { |name, guest| strongswan.eql? guest.strongswan }
+      end
+    end
+
+    # Return all kernel configurations that are referenced by any guest.
+    def kernels
+      @kernels.select do |name, kernel|
+        @guests.any? { |name, guest| kernel.eql? guest.kernel }
+      end
+    end
+
+    # Return all valid guest configurations.
+    def guests
+      @guests.reject { |name, guest| guest.invalid }
+    end
+
+  private
+
+    # Reads the config file. The file is processed with ERB before being
+    # loaded by YAML and converted into an OpenStruct.
+    def read_config(file)
+      require 'yaml'
+      require 'erb'
+      OpenStruct.new(YAML::load(ERB.new(IO.read(file)).result))
+    end
+
+    # Initializes the kernel configurations.
+    def prepare_kernels
+      @config.kernels ||= []
+      @kernels = @config.kernels.inject({}) do |h, conf|
+        name, conf = *conf
+        h[name] = KernelConfig.new name, OpenStruct.new(conf)
+        h
+      end
+    end
+
+    # Initializes the strongswan configurations.
+    def prepare_strongswan
+      @config.strongswan ||= []
+      @strongswan = @config.strongswan.inject({}) do |h, conf|
+        name, conf = *conf
+        h[name] = StrongswanConfig.new name, OpenStruct.new(conf)
+        h
+      end
+    end
+
+    # Initializes the guest configurations.
+    def prepare_guests
+      guests = OpenStruct.new @config.guests
+      defaults = read_guest_config("guests_defaults", guests.defaults)
+      guests.hosts ||= []
+      @guests = guests.hosts.inject({}) do |h, conf|
+        name, conf = conf.is_a?(Hash) ? conf.shift : [conf, {}]
+        conf = read_guest_config("guests_#{name}", conf).delete_if { |k, v| v == nil }
+        conf = defaults.merge(conf)
+        h[name] = GuestConfig.new name, OpenStruct.new(conf)
+        h
+      end
+    end
+
+    # Read a guest config and return it as Hash.
+    def read_guest_config(name, config)
+      c = OpenStruct.new config
+      h = OpenStruct.new
+      h.strongswan = if c.strongswan && c.strongswan.is_a?(Hash)
+                       @strongswan[name] = StrongswanConfig.new name, c.strongswan
+                     else
+                       @strongswan[c.strongswan]
+                     end
+      h.kernel = if c.kernel && c.kernel.is_a?(Hash)
+                   @kernels[name] = KernelConfig.new name, c.kernel
+                 else
+                   @kernels[c.kernel]
+                 end
+      h.masterfs = c.masterfs if c.masterfs && (Testing.tarball?(c.masterfs) || File.directory?(c.masterfs))
+      h.templates = c.templates if c.templates && (c.templates.empty? || File.directory?(c.templates))
+      h.mem = c.mem.to_i if c.mem
+      h.consoles = c.consoles.select { |c| c =~ /^(xterm|pts)$/ } if c.consoles
+      h.marshal_dump
+    end
+
+  end
+end
diff --git a/testing/lib/console_format.rb b/testing/lib/console_format.rb
new file mode 100644 (file)
index 0000000..bdc7dc9
--- /dev/null
@@ -0,0 +1,128 @@
+=begin
+  Copyright (C) 2008 Tobias Brunner
+  Hochschule fuer Technik Rapperswil
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by the
+  Free Software Foundation; either version 2 of the License, or (at your
+  option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+  for more details.
+
+  $Id$
+=end
+
+# Simplifies formatting console output by using ANSI escape codes
+
+module Dumm
+  module ConsoleFormat
+    @@colors = {
+      :black => 0,
+      :red => 1,
+      :green => 2,
+      :yellow => 3,
+      :blue => 4,
+      :magenta => 5,
+      :cyan => 6,
+      :white => 7
+    }
+    @@colors.default = 9
+    
+    @@intensity = { :bold => 1 }
+    @@intensity.default = 22
+    
+    @@blink = { :slow => 5, :rapid => 6 }
+    @@blink.default = 25
+    
+    @@underline = { :single => 4, :double => 21 }
+    @@underline.default = 24
+    
+    @@codes = @@colors.inject({}) { |codes, color|
+      codes[color[0]] = 30 + color[1]
+      codes[("b#{color[0]}".to_s).to_sym] = 40 + color[1]
+      codes
+    }
+    @@codes = @@codes.merge(@@intensity).merge(@@blink).merge(@@underline)
+    @@keywords = @@codes.keys.join("|")
+    
+    # returns the text either with or without ansi codes wrapped around
+    # depending on whether stdout is currently attached to a tty.
+    # FIXME: what if we want to write the text to a file i.e. not stdout
+    def self.format_text(text, code, force = false)
+      STDOUT.isatty || force ? "\e[0#{code}m#{text}\e[0m" : text
+    end
+
+    # format is a hash with the following parameters:
+    #  - :color: foreground color
+    #  - :background: background color
+    #  - :intensity: :bold
+    #  - :blink: :slow, :rapid
+    #  - :underline: :single, :double
+    def self.format(text, format = {}, force = false)
+      code = ";#{@@blink[format[:blink]]}"
+      code += ";#{@@underline[format[:underline]]}"
+      code += ";#{@@intensity[format[:intensity]]}"
+      code += ";3#{@@colors[format[:color]]}"
+      code += ";4#{@@colors[format[:background]]}"
+      format_text(text, code, force)
+    end
+    
+    # catches formats of the form:
+    #   red_bwhite("red on white")
+    #   red_bold("red bold")
+    #   red_single("red underline single")
+    #   ...
+    # background colors start with a 'b'
+    def self.method_missing(method, text)
+      method = method.to_s
+      if method =~ /^(#{@@keywords})(_(#{@@keywords}))*$/
+        code = method.split(/_/).inject("") { |c, format|
+          c += ";#{@@codes[format.to_sym]}"
+        }
+        format_text(text, code)
+      end
+    end
+    
+    module ConsoleFormatWrapper
+      def self.method_missing(method, text)
+        ConsoleFormat.__send__ method, text
+      end
+    end
+    
+    # global shortcut function 'fmt'
+    module ::Kernel
+      def fmt(text = nil, format = {}, force = false)
+        if text
+          ConsoleFormat.format(text, format, force)
+        else
+          ConsoleFormatWrapper
+        end
+      end
+    end
+  end
+  
+  # Test
+  if __FILE__ == $0
+    fmt "blank"
+    fmt "red", { :color => :red }
+    fmt.red_bold "red bold"
+    fmt.red_blue_byellow_single_bold "blue on yellow underline single_bold"
+    puts ConsoleFormat.format("red", { :color => :red })
+    puts ConsoleFormat.format("blue background", { :background => :blue })
+    puts ConsoleFormat.format("red on white background", { :color => :red, :background => :white })
+    puts ConsoleFormat.format("red bold", { :color => :red, :intensity => :bold })
+    puts ConsoleFormat.format("red blink (slow)", { :color => :red, :intensity => :bold, :blink => :slow })
+    puts ConsoleFormat.format("red bold blink (rapid)", { :color => :red, :intensity => :bold, :blink => :rapid })
+    puts ConsoleFormat.format("green underline single", { :color => :green, :underline => :single })
+    puts ConsoleFormat.format("green underline double", { :color => :green, :underline => :double })
+    puts ConsoleFormat.red_bold("red bold")
+    puts ConsoleFormat.red_bwhite_bold("red on white bold")
+    puts ConsoleFormat.red_single("red underline single")
+    puts ConsoleFormat.bold_red_single("red underline single bold")
+    puts ConsoleFormat.black_bwhite("black on white")
+  end
+end
+
diff --git a/testing/lib/guest_config.rb b/testing/lib/guest_config.rb
new file mode 100644 (file)
index 0000000..21ec2e5
--- /dev/null
@@ -0,0 +1,70 @@
+=begin
+  Copyright (C) 2009 Tobias Brunner
+  Hochschule fuer Technik Rapperswil
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by the
+  Free Software Foundation; either version 2 of the License, or (at your
+  option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+  for more details.
+=end
+
+module Dumm
+  class GuestConfig
+    # The name of this guest config.
+    attr_reader :name
+    # The strongSwan config.
+    attr_reader :strongswan
+    # The kernel config
+    attr_reader :kernel
+    # The root directory of this guest (i.e. the diff to the master).
+    attr_reader :root
+    # True if this guest config is invalid
+    attr_reader :invalid
+
+    def initialize(name, config)
+      @name = name
+      @needs_build = true
+      @strongswan, @kernel = config.strongswan, config.kernel
+      @masterfs, @templates = config.masterfs, config.templates
+      @mem, @consoles = config.mem, config.consoles
+      unless @strongswan && @kernel && @masterfs && @mem > 0 && @consoles && !@consoles.empty?
+        puts "Invalid guest configuration: #{name} #{config.inspect}"
+        @invalid = true
+      end
+    end
+
+    def build
+      return unless @needs_build
+      args = "mem=#{@mem}M"
+      @consoles.each_with_index do |con, i|
+        args << " con#{i.next}=#{con}"
+      end
+      # TODO if the masterfs is a tarball, extract that first
+      #Dir.chdir(Testing.root) do
+      #  Guest.new @name, @kernel.path || "/home/tbrunner/tbrunner/testing/umlbuild/linux-uml-2.6.21.1", @masterfs, args
+      #end
+      Dir.chdir(Testing.guests_dir) do
+        Dir.mkdir(name, 0775)
+        Dir.chdir(name) do
+          File.symlink(@masterfs, 'master')
+          File.symlink(@kernel.path, 'linux')
+          Dir.mkdir('diff', 0775)
+          Dir.mkdir('union', 0775)
+          File.open('args', 'w') { |f| f.write args }
+        end
+      end
+      @root = File.join(Testing.guests_dir, name, 'diff')
+      tmpl = File.join(@templates, name)
+      if File.directory?(tmpl)
+        FileUtils.cp_r(tmpl + '/.', @root) # '/.' is required to copy the contents of tmpl and not tmpl itself
+      end
+      @needs_build = false
+    end
+
+  end
+end
diff --git a/testing/lib/kernel_config.rb b/testing/lib/kernel_config.rb
new file mode 100644 (file)
index 0000000..183c785
--- /dev/null
@@ -0,0 +1,118 @@
+=begin
+  Copyright (C) 2009 Tobias Brunner
+  Hochschule fuer Technik Rapperswil
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by the
+  Free Software Foundation; either version 2 of the License, or (at your
+  option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+  for more details.
+=end
+
+module Dumm
+  class KernelConfig
+    # The name of this kernel
+    attr_reader :name
+    # The path to the kernel
+    attr_reader :path
+
+    # Creates a new Kernel instance from the given configuration
+    def initialize(name, config)
+      @name = name
+      @needs_build = true
+      @source = (config.source || "none").to_sym
+      @config = config.config if File.file?(config.config || "")
+      config.patches ||= []
+      @patches = config.patches.select { |p| File.exists?(p) && p =~ /\.patch(\.(bz2|gz))?$/ }
+      case @source
+      when :git, :dir
+        unless File.directory?(config.path)
+          raise "Path '#{config.path}' not found!"
+        end
+        @source_path = config.path
+        unless @source == :dir
+          raise "'#{@source_path} is not a git repository!" unless Testing.git?(@source_path)
+          @checkout = config.checkout if Testing.git_tree?(@source_path, config.checkout)
+        end
+      when :tar
+        raise "Tarball '#{config.path}' not found!" unless Testing.tarball?(config.path)
+        @source_path = config.path
+      else
+        raise "Kernel '#{config.path}' not found!" unless File.executable?(config.path)
+        @path = config.path
+        @needs_build = false
+      end
+    end
+
+    # Build the kernel.
+    def build
+      return unless @needs_build
+      build_path = @source_path
+      case @source
+      when :git
+        if @checkout
+          tarball = Testing.archive_git(@source_path, @checkout, "kernel-#{@name}", Testing.build_dir)
+          build_path = Testing.extract_tarball(tarball, Testing.build_dir)
+        end
+      when :tar
+        build_path = Testing.extract_tarball(@source_path, Testing.build_dir)
+      end
+
+      apply_patches(build_path)
+      ensure_config(build_path)
+      @path = File.join(Testing.build_dir, "kernel-#{name}-linux")
+      build_kernel(build_path, @path)
+
+      # TODO we could remove the directory extracted from a tarball and the
+      # tarball itself if it was exported from git.
+
+      @needs_build = false
+    end
+
+  private
+
+    # Apply a list of patches to the given source tree.
+    def apply_patches(dir)
+      Dir.chdir(dir) do
+        @patches.each do |patch|
+          comp = case patch
+                   when /\.bz2$/: 'bz'
+                   when /\.gz$/: 'z'
+                   else ''
+                 end
+          `#{comp}cat #{patch} | patch -p1 2>&1`
+          unless $?.success?
+            raise "Failed to apply patch '#{patch}' in '#{dir}'!"
+          end
+        end
+      end
+    end
+
+    # Ensure that we have a kernel config.
+    def ensure_config(dir)
+      config = File.join(dir, ".config")
+      @config ||= config
+      raise "No kernel config found!" unless File.file?(@config)
+      FileUtils.copy_file(@config, config)
+    end
+
+    # Build the kernel and move it to the given location.
+    def build_kernel(dir, kernel)
+      Dir.chdir(dir) do
+        # TODO what about logging
+        `make clean ARCH=um 2>&1`
+        # the next command might interact with the user. since '`' redirects
+        # stdout we would have to use 'system'. currently we use 'yes' to
+        # chose the default value for new kernel options.
+        `yes "" | make oldconfig ARCH=um 2>&1`
+        `make -j 2 linux ARCH=um 2>&1`
+        FileUtils.mv 'linux', kernel
+      end
+    end
+
+  end
+end
diff --git a/testing/lib/strongswan_config.rb b/testing/lib/strongswan_config.rb
new file mode 100644 (file)
index 0000000..44058ce
--- /dev/null
@@ -0,0 +1,108 @@
+=begin
+  Copyright (C) 2009 Tobias Brunner
+  Hochschule fuer Technik Rapperswil
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by the
+  Free Software Foundation; either version 2 of the License, or (at your
+  option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+  for more details.
+=end
+
+module Dumm
+  class StrongswanConfig
+    # The name of this strongSwan config.
+    attr_reader :name
+
+    def initialize(name, config)
+      @name = name
+      @needs_build = true
+      @options = config.options
+      @source = (config.source || "none").to_sym
+      case @source
+      when :git, :dir
+        unless File.directory?(config.path)
+          raise "Path '#{config.path}' not found!"
+        end
+        @source_path = config.path
+        unless @source == :dir
+          raise "'#{@source_path} is not a git repository!" unless Testing.git?(@source_path)
+          @checkout = config.checkout if Testing.git_tree?(@source_path, config.checkout)
+        end
+      when :tar
+        raise "Tarball '#{config.path}' not found!" unless Testing.tarball?(config.path)
+        @source_path = config.path
+      else
+        raise "Specify the source type of strongSwan config '#{name}'"
+      end
+    end
+
+    # Build the strongSwan sources. We build them only within the source tree
+    # if we extracted the sources from a tarball. Otherwise an out-of-tree build
+    # in a subdir of the testing build dir is done.
+    def build
+      return unless @needs_build
+      @build_path = File.join(Testing.build_dir, "strongswan-#{name}")
+      case @source
+      when :git
+        if @checkout
+          tarball = Testing.archive_git(@source_path, @checkout, "strongswan-#{@name}", Testing.build_dir)
+          @source_path = @build_path = Testing.extract_tarball(tarball, Testing.build_dir)
+        end
+      when :tar
+        @source_path = @build_path = Testing.extract_tarball(@source_path, Testing.build_dir)
+      end
+
+      FileUtils.mkdir_p(@build_path)
+      configure(@source_path, @build_path)
+      make(@build_path)
+
+      @needs_build = false
+    end
+
+    def install(target)
+      Dir.chdir(@build_path) do
+        `make install DESTDIR="#{target}" 2>&1`
+        raise "Failed to install strongSwan '#{name}'!" unless $?.success?
+        # FIXME is ldconfig required? how do we run this
+      end
+    end
+
+  private
+
+    # Run the configure script located in directory 'sources' within the
+    # directory given as 'build'. autogen.sh is run if configure does not yet
+    # exist.
+    def configure(sources, build)
+      script = File.join(sources, "configure")
+      unless File.executable?(script)
+        Dir.chdir(sources) do
+          `./autogen.sh 2>&1`
+          raise "Failed to build configure script for strongSwan '#{name}'!" unless $?.success?
+        end
+      end
+      options = [ '--sysconfdir=/etc', '--with-random-device=/dev/urandom' ]
+      @options.each do |opt|
+        key, val = opt.shift
+        options << "--#{val ? 'enable' : 'disable'}-#{key.sub(/_/, '-')}"
+      end
+      Dir.chdir(build) do
+        `#{script} #{options.join(" ")} 2>&1`
+        raise "Failed to configure strongSwan '#{name}'!" unless $?.success?
+      end
+    end
+
+    # Build the strongSwan sources.
+    def make(build)
+      Dir.chdir(build) do
+        `make -j 2>&1`
+        raise "Failed to build strongSwan '#{name}'!" unless $?.success?
+      end
+    end
+
+  end
+end
diff --git a/testing/lib/testing.rb b/testing/lib/testing.rb
new file mode 100644 (file)
index 0000000..7444cd4
--- /dev/null
@@ -0,0 +1,152 @@
+=begin
+  Copyright (C) 2009 Tobias Brunner
+  Hochschule fuer Technik Rapperswil
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by the
+  Free Software Foundation; either version 2 of the License, or (at your
+  option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+  for more details.
+=end
+
+$LOAD_PATH.unshift File.dirname(__FILE__)
+require 'config'
+
+module Dumm
+  class Testing
+    class << self
+      # The base directory.
+      attr_reader :root
+      # The build directory.
+      attr_reader :build_dir
+      # The guests directory.
+      attr_reader :guests_dir
+      # The global Config instance
+      attr_reader :config
+
+      def init
+        set_directories!
+        config_file = "#{root}/config/default.yml"
+        @config = Config.new(config_file)
+      end
+
+      # Create the testing environment.
+      def make
+        make_kernel
+        make_guests
+        make_strongswan
+      end
+
+      # Check if the given file is a tarball.
+      def tarball?(file)
+        file && File.file?(file) && file =~ /(\.tar(\.(bz2|gz))?|\.t[bg]z)$/
+      end
+
+      # Returns the name of the tarball without extension and path.
+      # This is the assumed name of the directory that the tarball extracts to.
+      def tarball_name(file)
+        file.sub(/^.*\/([^\/]+)(\.tar(\.(bz2|gz))?|\.t[bg]z)$/, '\1')
+      end
+
+      # Check the given path for a git repository. If 'tree' is given we check
+      # that there exists such a point in the git history.
+      def git?(dir)
+        return false unless File.directory?(dir)
+        Dir.chdir(dir) do
+          !`git status`.empty?
+        end
+      end
+
+      # Check that tree points to a valid point in the history of the git
+      # repository (commit, tag, branch).
+      def git_tree?(dir, tree)
+        return false unless File.directory?(dir)
+        Dir.chdir(dir) do
+          tree && !tree.empty? && !`git show #{tree}`.empty?
+        end
+      end
+
+      # Extract the given tarball in directory 'dir'. The tarball is expected
+      # to extract into a directory of the same name. Returns the path to the
+      # extracted directory.
+      # If the expected directory already exists, nothing is done.
+      def extract_tarball(file, dir)
+        target = File.join(dir, tarball_name(file))
+        return target if File.directory?(target)
+        Dir.chdir(dir) do
+          comp = case file
+                   when /\.(bz2|tbz)$/: 'j'
+                   when /\.(gz|tgz)$/: 'z'
+                   else ''
+                 end
+          unless system("tar x#{comp}f #{file} 2>&1")
+            raise "Failed to extract tarball '#{file}'!"
+          end
+          unless File.directory?(target)
+            raise "Tarball '#{file}' extracted to unexpected directory!"
+          end
+        end
+        target
+      end
+
+      # Uses 'git archive' to build a tarball from the git repository in
+      # directory 'repo'. 'tree' is the tag, branch or commit from which the
+      # archive is built. The tarball is written to 'dir'/'name'.tar and it will
+      # extract to a directory named 'name'. The filename of the tarball is
+      # returned.
+      def archive_git(repo, tree, name, dir)
+        target = File.join(dir, "#{name}.tar")
+        Dir.chdir(repo) do
+          unless system("git archive --format=tar --prefix=#{name}/ #{tree} > #{target}")
+            raise "Failed to build archive from git repository '#{git}'!"
+          end
+        end
+        target
+      end
+
+    private
+
+      # Sets root to TESTING_ROOT and canonalizes it. Also sets the build
+      # and guest dir based on the root.
+      def set_directories!
+        require 'pathname'
+        raise 'TESTING_ROOT is not set' unless defined?(::TESTING_ROOT)
+        raise 'TESTING_ROOT is not a directory' unless File.directory?(::TESTING_ROOT)
+        @root = Pathname.new(::TESTING_ROOT).realpath.to_s
+        ::TESTING_ROOT.replace @root
+        @build_dir = File.join(@root, 'build')
+        @guests_dir = File.join(@root, 'guests')
+      end
+
+      # Build all required kernels.
+      def make_kernel
+        config.kernels.each do |name, kernel|
+          kernel.build
+        end
+      end
+
+      # Create all the guests.
+      def make_guests
+        config.guests.each do |name, guest|
+          guest.build
+        end
+      end
+
+      # Build and install all strongSwan versions.
+      def make_strongswan
+        config.strongswan.each do |name, strongswan|
+          strongswan.build
+          guests = config.guests.select { |n, g| strongswan.eql? g.strongswan }
+          guests.each do |name, guest|
+            strongswan.install guest.root
+          end
+        end
+      end
+
+    end
+  end
+end
diff --git a/testing/make-testing.rb b/testing/make-testing.rb
new file mode 100755 (executable)
index 0000000..a75568b
--- /dev/null
@@ -0,0 +1,62 @@
+#!/usr/bin/ruby
+
+=begin
+  Copyright (C) 2009 Tobias Brunner
+  Hochschule fuer Technik Rapperswil
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by the
+  Free Software Foundation; either version 2 of the License, or (at your
+  option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+  for more details.
+=end
+
+#require 'dumm'
+require 'lib/testing'
+require 'fileutils'
+
+include Dumm
+
+# are we running as superuser
+unless Process.uid == 0
+  puts "Please run #{$0} as superuser!"
+  exit 1
+end
+
+TESTING_ROOT = File.dirname(__FILE__)
+
+Testing.init
+
+def continue?(msg)
+  puts msg
+  print "Continue? [Y|n]: "
+  if gets.capitalize =~ /^N.*/
+    exit 1
+  end
+end
+
+# cleanup the build dir
+build_dir = Testing.build_dir
+if File.directory?(build_dir)
+  continue?("The existing build directory #{build_dir} will be deleted!")
+  FileUtils.remove_entry_secure(build_dir, force = true)
+end
+FileUtils.mkdir_p(build_dir)
+
+# cleanup the guest dir
+guests_dir = Testing.guests_dir
+if File.directory?(guests_dir)
+  continue?("All guests in #{guests_dir} will be deleted!")
+  #Guest.each { |g| g.delete }
+  FileUtils.remove_entry_secure(guests_dir, force = true)
+end
+FileUtils.mkdir_p(guests_dir)
+
+Testing.make
+
+puts "built"
+