Skip to content

Instantly share code, notes, and snippets.

@NickLaMuro
Last active May 10, 2019 00:25
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save NickLaMuro/8438015363d90a2d2456be650a2b9bc6 to your computer and use it in GitHub Desktop.
Save NickLaMuro/8438015363d90a2d2456be650a2b9bc6 to your computer and use it in GitHub Desktop.
MIQ Vagrant environment for testing evm:db tasks (both local and remote, dump and backup)
.env
.vagrant/

Purpose

This is a Vagrantfile and test script that is intended to setup a reproducible environment with an appliance VM and a share VM. Those roles of the VMs are as follows:

  • The appliance VM is a MIQ appliance with the actually workers shut down, but preloaded with a dummy set of data that can be exported. It also includes a test script that can be used as a smoke test for all of the database backup/dumping strategies.
  • The share VM is a light appliance that is currently configured to be an NFS SMB, and FTP share for the appliance. It is configured to give permission to the appliance VM those to connect via SMB and NFS at some entry points (it also set up a swift instance, but that currently isn't configured properly to work)

The test script will then run a suite of all of the backup/dump strategies and confirm file integrity of the dumped files for all strategies (local, NFS, SMB, FTP, s3 and Swift .

Usage

Quick start

Run rake test and things should #JustWork™

The task should handle setting up your .env file for you with the needed credentials for s3 and swift testing (currently both require a remote instance for testing to happen), spin up the environment, seed, and run the specs. The tasks are configured to be as omnipotent as possible (within reason), so they should be limited amounts wasted time checking for "dependencies" (running VMs, checking for seeding, etc.)

Setup (long version)

The main steps that are done on first run of rake test are as follows:

  1. Run rake .env, which is a Rake::FileTask that configures the .env with s3 and swift credentials which are needed to run those specs currently. You can opt out of those tests by running rake test with the following:

    $ rake test:no_s3 test:no_swift test
    

    Which will skip any setup and tests for s3 (ideally...)

  2. Run rake start which effectively runs vagrant up for you. This task does do some VirtualBox "shell outs" to check if the vms are running first, so this will be a snappy task if nothing needs to be done.

  3. Run rake seed, which is an ssh task to the box that triggers the seed script that is provisioned into the box (see the Vagrantfile for more details). This task is a little slower even on subsequent runs since it requires running a bin/rails on the box (and that is just slow), but it is configure to short circut once it has one once, so it is only a minor delay the subsequent calls.

  4. rake test proper is run, which runs the following SSH command on the appliance vm:

    $ vagrant ssh appliance -c "sudo -i ruby /vagrant/tests/smoketest.rb --clean [EXTRA_ARGS]"
    

    As mentioned above, the EXTRA_ARGS comes from the test:no_s3 and test:no_swift tasks if applied, and --clean can be opted out of if test:no_clean is passed. Under the covers this is just mutating a instance variable in the Rakefile, so these tasks need to be run prior to the main test task to be populated in the command correctly.

Old Setup (outdated)

Leaving this here for reference, but since we are now using a hammer build for the appliance, most of the code necessary to run things is included with the base image.

Steps

  1. Copy the Vagrantfile and the smoketest_db_tasks.rb into a directory that is on the same level as the following repos:

    The resulting directory structure should look something like this:

    ├── dir_for_this_code
    │   ├── Vagrantfile
    │   └── smoketest.rb
    ├── manageiq
    │   ...
    ├── manageiq-gems-pending
    │   ...
    ├── manageiq-appliance_console
    │   ...
    └── awesome_spawn
    
  2. In the directory with the Vagrantfile, run:

    $ vagrant up
    $ vagrant ssh appliance
    appliance $ vmdb
    appliance $ sudo /bin/sh -c "source /etc/profile.d/evm.sh; bin/rails r tmp/bz_1592480_db_replication_script.rb"
    appliance $ exit
  3. To run the test, run the following:

    $ vagrant ssh appliance -c "sudo -i ruby /vagrant/tests/smoketest.rb"

This does assume that the following pull requests have been merged for the smoke tests to succeed:

If they aren't merged, you can run the following to get that code in place:

$ CWD=$(pwd)
$ cd ../manageiq
$ git apply <(curl -L https://github.com/ManageIQ/manageiq/pull/17549.patch)
$ git apply <(curl -L https://github.com/ManageIQ/manageiq/pull/17652.patch)
$ cd ../awesome_spawn
$ git apply <(curl -L https://github.com/ManageIQ/awesome_spawn/pull/41.patch)
$ cd ../manageiq-gems-pending
$ git apply <(curl -L https://github.com/ManageIQ/manageiq-gems-pending/pull/356.patch)
$ cd $CWD

And then to setup the VMs to have this code, run the following:

$ vagrant rsync
$ vagrant provision appliance

S3 environment variable setup

You will need a .env file in the directory with the following:

AWS_REGION="us-east-1"
AWS_ACCESS_KEY_ID=[YOUR_ACCESS_KEY_ID_HERE]
AWS_SECRET_ACCESS_KEY=[YOUR_SECRET_ACCESS_KEY_HERE]

Creating and testing FileDepots

Creating and Testing NFS

$ vagrant ssh appliance
appliance(vagrant) $ vmdb
appliance(vagrant) $ sudo --shell
appliance(root) $ bin/rails c
irb> task = MiqTask.create
irb> file_depot = FileDepotNfs.create(:uri => "nfs://192.168.50.11")
irb> MiqServer.my_server.log_file_depot = file_depot
irb> MiqServer.my_server.save
irb> MiqServer.my_server.post_current_logs(task.id, file_depot)

Creating and Testing SMB

$ vagrant ssh appliance
appliance(vagrant) $ vmdb
appliance(vagrant) $ sudo --shell
appliance(root) $ bin/rails c
irb> task = MiqTask.create
irb> smb_auth = Authentication.new(:userid => "vagrant", :password => "vagrant")
irb> file_depot = FileDepotSmb.create(:uri => "smb://192.168.50.11/share", :authentications => [smb_auth])
irb> MiqServer.my_server.log_file_depot = file_depot
irb> MiqServer.my_server.save
irb> MiqServer.my_server.post_current_logs(task.id, file_depot)

TROUBLESHOOTING

  • If most of the tests for splits are failing:

    Chances are in this case the appliance might still be running, causing not only a decent delay in the tests between a non-split backup (where the base size for the backup is derived) and the split version, which gives the DB backup more time to grow in size because of split CPU resources, but also extra log statements are created from general appliance actions, causing the size to be bigger as well.

    Run the following to check if the appliance is still running:

    $ vagrant ssh appliance -c "sudo systemctl status evmserverd"

    And if it is, run:

    $ vagrant ssh appliance -c "sudo systemctl stop evmserverd"
    $ vagrant ssh appliance -c "sudo systemctl disable evmserverd"
  • If split backup tests are still failing

    As much as I have done to try an mitigate this, it does happen from time to time.

    I think part of it is that when you wait to run tests for a while, the database log might get "back logged", and cause a considerable amount extra bytes to exist on the DB log.

    Another alternative (facts) theory is that postgresql/the appliance database configuration might have decided to run a background task just after you were running the previous test and caused the split backup test in question to be much larger.

    Try re-running and seeing if it passes. These tests with the backup aren't fool proof since they just checking that they byte size of the file is "mostly" the same since it is quicker than reloading the DB... though we do now do that....

  • The first test is running indefinitely

    I haven't been able to narrow this one down yet, but it seems like restarting the test suite fixes this issue, and it only seems to happen on the first run of the suite after the VM has booted.

    Any ideas as to WHY this behavior is occurring would be appreciated, but my guess is no one is actually reading this anyway... so I am probably on my own for this one.

  • Aws::S3::Errors::RequestTimeTooSkewed

    You might get this if your VM has been turned on in between shutting of your laptop lid, or other causes (the VMs are setup to re-sync the internal clock on resume).

    Best to just restart the VMs with a vagrant halt && vagrant up

require_relative "./appliance_secondary_db.rb"
require "linux_admin"
# Wrap LinuxAdmin::Service to handle starting/stopping postgres when this file
# is loaded.
module LinuxAdmin
def Service.new(service_name)
if PostgresAdmin.service_name == service_name
ApplianceSecondaryDB
else
super
end
end
end
require "postgres_admin"
# Patch PostgresAdmin to use different user/group
class PostgresAdmin
def self.user
"vagrant".freeze
end
end
ENV["APPLIANCE_PG_DATA"] = "/opt/manageiq/postgres_restore_pg/data"
ENV["APPLIANCE_PG_SERVICE"] = "local_pg_instance"
require "etc"
require "fileutils"
class ApplianceSecondaryDB
extend FileUtils
PG_DIR = File.join("", "opt", "manageiq", "postgres_restore_pg").freeze
RUN_DIR = File.join(PG_DIR, "run").freeze
DATA_DIR = File.join(PG_DIR, "data").freeze
def self.start
update_config
run_cmd "pg_ctl start -D #{DATA_DIR} -wo '-p 5555'"
end
def self.stop
run_cmd "pg_ctl stop -D #{DATA_DIR} -wm fast"
end
def self.reset_db
stop
rebuild_data_dir
start
create_roles
end
def self.run_cmd cmd
cmd = %Q{su vagrant -lc \"#{cmd}\"} if Etc.getlogin == "vagrant"
system cmd, [:out, :err] => File::NULL
end
def self.rebuild_data_dir
rm_rf Dir["#{DATA_DIR}/*"]
mkdir_p DATA_DIR
mkdir_p RUN_DIR
chown_R "vagrant", "vagrant", DATA_DIR
chown_R "vagrant", "vagrant", RUN_DIR
run_cmd "pg_ctl initdb -D #{DATA_DIR} -o '-A trust'"
end
def self.create_roles
run_cmd %Q{psql --port 5555 -h localhost -c \\"CREATE ROLE root WITH LOGIN CREATEDB SUPERUSER PASSWORD 'smartvm'\\" postgres}
run_cmd %Q{psql --port 5555 -h localhost -c \\"CREATE ROLE postgres\\" postgres}
end
def self.update_config
conf_file = File.join DATA_DIR, 'postgresql.conf'
conf = File.read conf_file
conf.gsub! "include_dir = '/etc/manageiq/postgresql.conf.d'", "ssl = on"
conf << "\n\nunix_socket_directories = '#{RUN_DIR}, /tmp'"
File.write conf_file, conf
end
end
# Require 'tmpdir' and override the result for `Dir.tmpdir` to be `/fake_tmp`
# for the duration of the ruby execution.
#
# To be used with `RUBYOPT` when running commands
require 'tmpdir'
# Doing this two ways for maximum coverage, since @systmpdir.dup is used if
# $SAFE > 0 and the ENV['TMPDIR'] has priority over @systmpdir if $SAFE = 0.
Dir.instance_variable_set(:@systmpdir, "/fake_tmp")
ENV['TMPDIR'] = "/fake_tmp"
VMS = %w[appliance share]
VMDB_DIR = "/var/www/miq/vmdb"
VAGRANT_SSH_CMD = 'vagrant ssh %s -c "%s"'
def vm_running? vmname
state = read_vm_state vmname
state == :running
end
VMSTATE_CMD_TEMPLATE = "VBoxManage showvminfo " \
"$(VBoxManage list vms | grep %s | cut -d ' ' -f 2) " \
"--machinereadable"
# ripped from vagrant virtualbox driver read_state
def read_vm_state vmname
full_name = "appliance_smoketest_db_rake_tasks_#{vmname}"
cmd = VMSTATE_CMD_TEMPLATE % [full_name]
output = `#{cmd}`
if output =~ /^name="<inaccessible>"$/
return :inaccessible
elsif output =~ /^VMState="(.+?)"$/
return $1.to_sym
end
nil
end
desc "Setup"
task :setup => [".env", :seed]
desc "Seed the appliance DB"
task :seed => :start do
bin_rails = "#{VMDB_DIR}/bin/rails"
seed_script = "#{VMDB_DIR}/tmp/bz_1592480_db_replication_script.rb"
ssh_cmd = "sudo -i #{bin_rails} r #{seed_script}"
sh VAGRANT_SSH_CMD % [ "appliance", ssh_cmd ]
end
desc "Start the boxes"
task :start do
vms_to_start = VMS.reject { |vmname| vm_running? vmname }
if vms_to_start.empty?
puts "vms already running..."
else
start_cmd = "vagrant up"
start_cmd << " #{vms_to_start.first}" if vms_to_start.size == 1
# Must run through rake since it is a prereq for other tasks
sh start_cmd
end
end
desc "Stop the boxes"
task :stop do
vms_to_stop = VMS.select { |vmname| vm_running? vmname }
if vms_to_stop.empty?
puts "vms already stopped..."
else
exec "vagrant halt"
end
end
task :halt => :stop
desc "Destroy the boxes"
task :destroy do
exec "vagrant destroy --force"
end
desc "Rebuild all boxes"
task :reset do
exec "vagrant destroy --force && vagrant up"
end
namespace :reset do
desc "Rebuild the appliance box"
task :appliance do
exec "vagrant destroy appliance --force && vagrant up appliance"
end
desc "Rebuild the share box"
task :share do
exec "vagrant destroy share --force && vagrant up share"
end
end
desc "Run the tests"
task :test => [:setup, "test:test_args"] do
test_cmd = "sudo -i ruby /vagrant/tests/smoketest.rb "
test_cmd << @test_args.join(" ")
sh VAGRANT_SSH_CMD % [ "appliance", test_cmd ]
end
namespace :test do
# Setup the @test_args var
task :test_args do
# @test_args = ["--restore-from-orig"]
@test_args = ["--clean"]
end
desc "Skip S3 specs (run prior to `rake test`)"
task :no_s3 => :test_args do
@test_args << "--no-s3"
end
desc "Skip Swift specs (run prior to `rake test`)"
task :no_swift => :test_args do
@test_args << "--no-swift"
end
desc "Skip cleaning process (run prior to `rake test`)"
task :no_clean => :test_args do
@test_args.reject { |arg| arg == "--clean" }
end
end
file ".env" do |file|
require 'io/console'
puts "Setting up .env file..."
puts "-----------------------"
puts
puts "Note: 'required' vars are necessary for the specific provider to work."
puts "If you don't have those particular variables available, make sure to"
puts "skip those tests when running the test suite."
puts
defaults = <<-DOT_ENV.gsub(/^\s*/, '')
AWS_REGION="us-east-1"
AWS_ACCESS_KEY_ID="CHANGEME"
AWS_SECRET_ACCESS_KEY="CHANGEME"
SWIFT_HOST="REQUIRED"
SWIFT_TENANT="admin"
SWIFT_USERNAME="admin"
SWIFT_PASSWORD="CHANGEME"
SWIFT_REGION=""
SWIFT_SECURITY_PROTOCOL="non-ssl"
SWIFT_API_VERSION="v3"
SWIFT_DOMAIN_ID="default"
DOT_ENV
File.open file.name, "w" do |io|
defaults.lines.each do |line|
name, default = line.split "="
not_hidden = default.tap(&:chomp!) != '"CHANGEME"'
has_default = !%w["REQUIRED" ""].include?(default)
prompt = "Enter value for #{name}"
prompt << " (default: #{default})" if has_default && not_hidden
prompt << " (required)" if default == '"REQUIRED"'
prompt << ": "
print prompt
input = if not_hidden
STDIN.gets.chomp
else
STDIN.cooked { |io| io.noecho(&:gets) }.chomp.tap { puts }
end
value = input.size > 0 ? input : default.tr('"', '')
io.puts "#{name}=#{value.inspect}"
end
end
end
require 'optparse'
require_relative "./smoketest_config.rb"
OptionParser.new do |opts|
opts.banner = "Usage: smoketest.rb [options]"
opts.on("--clean", "Clean old artifacts from tests before run (def: false)") do |clean|
TestConfig.clean = clean
end
opts.on("--[no-]s3", "Run/Skip s3 functionality (def: run)") do |use_s3|
TestConfig.include_s3 = use_s3
end
opts.on("--[no-]swift", "Run/Skip swift functionality (def: run)") do |use_swift|
TestConfig.include_swift = use_swift
end
opts.on("--[no-]backups", "Run/Skip backup functionality (def: run)") do |skip|
TestConfig.skip_backups = !skip
end
opts.on("--[no-]dumps", "Run/Skip dump functionality (def: run)") do |skip|
TestConfig.skip_dumps = !skip
end
opts.on("--[no-]splits", "Run/Skip split functionality (def: run)") do |skip|
TestConfig.skip_splits = !skip
end
opts.on("--[no-]restore", "Run/Skip testing restores (def: skip)") do |skip|
TestConfig.skip_restore = !skip
end
opts.on("--restore-from-orig", "Run restores from their downloaded dir (def: false)") do
TestConfig.restore_from_original_dir = true
end
opts.on("-h", "--help") { puts opts.help; exit 1 }
end.parse!
require_relative "./smoketest_test_helper.rb"
clean
require_relative "./smoketest_db_tasks.rb"
require_relative "./smoketest_appliance_console.rb"
original_console_backup_size = nil
console_local_backup_file = "console_full_local_backup.tar.gz"
testing "appliance_console: Local Database Backup" do
ApplianceConsoleRunner.backup console_local_backup_file
assert_file_exists console_local_backup_file
original_console_backup_size = get_file_size console_local_backup_file
end
# --------------------------------------------- #
# console_split_local_backup_file = "console_split_local_backup.tar.gz"
# testing "appliance_console: Split Local Database Backup" do
# ApplianceConsoleRunner.backup console_split_local_backup_file, :local, "2M"
# assert_split_files console_split_local_backup_file, 2 * MEGABYTES, original_console_backup_size, 200
# end
# --------------------------------------------- #
testing "appliance_console: Local Database Backups are valid" do
DbTestCase.valid_database? console_local_backup_file
# console_split_local_backup_file
end
# --------------------------------------------- #
console_local_backup_file_with_subdir = "tmp/subdir/console_full_local_backup.tar.gz"
testing "appliance_console: Local Database Backup in subdir" do
ApplianceConsoleRunner.backup console_local_backup_file_with_subdir
assert_file_exists console_local_backup_file_with_subdir
DbTestCase.valid_database? console_local_backup_file_with_subdir
end
# --------------------------------------------- #
original_console_nfs_backup_size = nil
console_nfs_backup_file = "console_full_nfs_backup.tar.gz"
testing "appliance_console: NFS Database Backup" do
ApplianceConsoleRunner.backup console_nfs_backup_file, :nfs
run_in_mount :nfs do |mount_point|
mount_file = File.join(mount_point, "db_backup", console_nfs_backup_file)
assert_file_exists mount_file
original_console_nfs_backup_size = get_file_size mount_file
end
end
# --------------------------------------------- #
# console_split_nfs_backup_file = "console_split_nfs_backup.tar.gz"
# testing "appliance_console: Split NFS Database Backup" do
# ApplianceConsoleRunner.backup console_split_nfs_backup_file, :nfs, "2M"
# run_in_mount :nfs do |mount_point|
# mount_file = File.join(mount_point, "db_backup", console_split_nfs_backup_file)
# assert_split_files mount_file, 2 * MEGABYTES, original_console_nfs_backup_size, 200
# end
# end
# --------------------------------------------- #
testing "appliance_console: NFS Database Backups are valid" do
DbTestCase.valid_database? console_nfs_backup_file
# console_split_nfs_backup_file
end
# --------------------------------------------- #
original_console_smb_backup_size = nil
console_smb_backup_file = "console_full_smb_backup.tar.gz"
testing "appliance_console: SMB Database Backup" do
ApplianceConsoleRunner.backup console_smb_backup_file, :smb
run_in_mount :smb do |mount_point|
mount_file = File.join(mount_point, "db_backup", console_smb_backup_file)
assert_file_exists mount_file
original_console_smb_backup_size = get_file_size mount_file
end
end
# --------------------------------------------- #
# console_split_smb_backup_file = "console_split_smb_backup.tar.gz"
# testing "appliance_console: Split SMB Database Backup" do
# ApplianceConsoleRunner.backup console_split_smb_backup_file, :smb, "2M"
# run_in_mount :smb do |mount_point|
# mount_file = File.join(mount_point, "db_backup", console_split_smb_backup_file)
# assert_split_files mount_file, 2 * MEGABYTES, original_console_smb_backup_size, 200
# end
# end
# --------------------------------------------- #
testing "appliance_console: SMB Database Backups are valid" do
DbTestCase.valid_database? console_smb_backup_file
# console_split_smb_backup_file
end
# --------------------------------------------- #
original_console_s3_backup_size = nil
console_s3_backup_file = "console_full_s3_backup.tar.gz"
testing "appliance_console: S3 Database Backup" do
ApplianceConsoleRunner.backup console_s3_backup_file, :s3
s3_file = File.join("db_backup", console_s3_backup_file)
assert_file_exists s3_file
original_console_s3_backup_size = get_file_size s3_file
# Download DB to NFS share for validation later
run_in_mount(:nfs) { |mnt| S3Helper.download_to mnt, s3_file }
end
# --------------------------------------------- #
# console_split_s3_backup_file = "console_split_s3_backup.tar.gz"
# testing "appliance_console: Split S3 Database Backup" do
# ApplianceConsoleRunner.backup console_split_s3_backup_file, :s3, "2M"
# s3_file = File.join("db_backup", console_split_s3_backup_file)
# assert_file_exists s3_file
# assert_split_files s3_file, 2 * MEGABYTES, original_console_s3_backup_size, 300
# # Download DB to NFS share for validation later
# run_in_mount(:nfs) { |mnt| S3Helper.download_to mnt, s3_file }
# end
# --------------------------------------------- #
testing "appliance_console: S3 Database Backups are valid" do
DbTestCase.valid_database? console_s3_backup_file
# console_split_s3_backup_file
end
# --------------------------------------------- #
original_console_ftp_backup_size = nil
console_ftp_backup_file = "console_full_ftp_backup.tar.gz"
testing "appliance_console: FTP Database Backup" do
ApplianceConsoleRunner.backup console_ftp_backup_file, :ftp
assert_file_exists console_ftp_backup_file
original_console_ftp_backup_size = get_file_size console_ftp_backup_file
end
# --------------------------------------------- #
# console_split_ftp_backup_file = "console_split_ftp_backup.tar.gz"
# testing "appliance_console: Split FTP Database Backup" do
# ftp_file = console_split_ftp_backup_file
# ApplianceConsoleRunner.backup ftp_file, :ftp, "2M"
# assert_file_exists ftp_file
# assert_split_files ftp_file, 2 * MEGABYTES, original_console_ftp_backup_size, 300
# end
original_console_ftp_anonymous_backup_size = nil
console_ftp_anonymous_backup_file = "console_full_ftp_anonymous_backup.tar.gz"
testing "appliance_console: Anonymous FTP Database Backup" do
ApplianceConsoleRunner.backup console_ftp_anonymous_backup_file, :ftp_anonymous
assert_file_exists console_ftp_anonymous_backup_file
original_console_ftp_anonymous_backup_size = get_file_size console_ftp_anonymous_backup_file
end
# --------------------------------------------- #
# console_split_ftp_anonymous_backup_file = "console_split_ftp_anonymous_backup.tar.gz"
# testing "appliance_console: Anonymous Split FTP Database Backup" do
# ftp_file = console_split_ftp_anonymous_backup_file
# ApplianceConsoleRunner.backup ftp_file, :ftp_anonymous, "2M"
# assert_file_exists ftp_file
# assert_split_files ftp_file, 2 * MEGABYTES, original_console_ftp_anonymous_backup_size, 200
# end
# --------------------------------------------- #
testing "appliance_console: Anonymous FTP Database Backups are valid" do
DbTestCase.valid_database? console_ftp_backup_file,
# console_split_ftp_backup_file,
console_ftp_anonymous_backup_file
# console_split_ftp_anonymous_backup_file
end
# --------------------------------------------- #
original_console_dump_size = nil
console_local_dump_file = "console_full_local_dump.tar.gz"
testing "appliance_console: Local Database Dump" do
ApplianceConsoleRunner.dump console_local_dump_file
assert_file_exists console_local_dump_file
original_console_dump_size = get_file_size console_local_dump_file
end
# --------------------------------------------- #
console_split_local_dump_file = "console_split_local_dump.tar.gz"
testing "appliance_console: Split Local Database Dump" do
ApplianceConsoleRunner.dump console_split_local_dump_file, :local, "2M"
assert_split_files console_split_local_dump_file, 2 * MEGABYTES, original_console_dump_size, 200
end
# --------------------------------------------- #
console_local_dump_file_without_ca = "console_full_local_dump_without_custom_attributes.tar.gz"
testing "appliance_console: Local Database Dump excluding custom_attributes" do
ApplianceConsoleRunner.dump_with_no_custom_attributes console_local_dump_file_without_ca
assert_file_exists console_local_dump_file_without_ca
DbTestCase.no_custom_attributes? console_local_dump_file_without_ca
end
# --------------------------------------------- #
testing "appliance_console: Local Database Dumps are valid" do
DbTestCase.valid_database? console_local_dump_file,
console_split_local_dump_file
end
# --------------------------------------------- #
console_local_dump_file_with_subdir = "tmp/subdir/console_full_local_dump.tar.gz"
testing "appliance_console: Local Database Dump in subdir" do
ApplianceConsoleRunner.dump console_local_dump_file_with_subdir
assert_file_exists console_local_dump_file_with_subdir
DbTestCase.valid_database? console_local_dump_file_with_subdir
end
# --------------------------------------------- #
original_console_nfs_dump_size = nil
console_nfs_dump_file = "console_full_nfs_dump.tar.gz"
testing "appliance_console: NFS Database Dump" do
ApplianceConsoleRunner.dump console_nfs_dump_file, :nfs
run_in_mount :nfs do |mount_point|
mount_file = File.join(mount_point, "db_dump", console_nfs_dump_file)
assert_file_exists mount_file
original_console_nfs_dump_size = get_file_size mount_file
end
end
# --------------------------------------------- #
console_split_nfs_dump_file = "console_split_nfs_dump.tar.gz"
testing "appliance_console: Split NFS Database Dump" do
ApplianceConsoleRunner.dump console_split_nfs_dump_file, :nfs, "2M"
run_in_mount :nfs do |mount_point|
mount_file = File.join(mount_point, "db_dump", console_split_nfs_dump_file)
assert_split_files mount_file, 2 * MEGABYTES, original_console_nfs_dump_size, 200
end
end
# --------------------------------------------- #
console_nfs_dump_file_without_ca = "console_nfs_dump_without_custom_attributes.tar.gz"
testing "appliance_console: NFS Database Dump excluding custom_attributes" do
ApplianceConsoleRunner.dump_with_no_custom_attributes console_nfs_dump_file_without_ca, :nfs
run_in_mount :nfs do |mount_point|
mount_file = File.join(mount_point, "db_dump", console_nfs_dump_file_without_ca)
assert_file_exists mount_file
DbTestCase.no_custom_attributes? console_nfs_dump_file_without_ca
end
end
# --------------------------------------------- #
testing "appliance_console: NFS Database Dumps are valid" do
DbTestCase.valid_database? console_nfs_dump_file,
console_split_nfs_dump_file
end
# --------------------------------------------- #
original_console_smb_dump_size = nil
console_smb_dump_file = "console_full_smb_dump.tar.gz"
testing "appliance_console: SMB Database Dump" do
ApplianceConsoleRunner.dump console_smb_dump_file, :smb
run_in_mount :smb do |mount_point|
mount_file = File.join(mount_point, "db_dump", console_smb_dump_file)
assert_file_exists mount_file
original_console_smb_dump_size = get_file_size mount_file
end
end
# --------------------------------------------- #
console_split_smb_dump_file = "console_split_smb_dump.tar.gz"
testing "appliance_console: Split SMB Database Dump" do
ApplianceConsoleRunner.dump console_split_smb_dump_file, :smb, "2M"
run_in_mount :smb do |mount_point|
mount_file = File.join(mount_point, "db_dump", console_split_smb_dump_file)
assert_split_files mount_file, 2 * MEGABYTES, original_console_smb_dump_size, 200
end
end
# --------------------------------------------- #
console_smb_dump_file_without_ca = "console_smb_dump_without_custom_attributes.tar.gz"
testing "appliance_console: SMB Database Dump excluding custom_attributes" do
ApplianceConsoleRunner.dump_with_no_custom_attributes console_smb_dump_file_without_ca, :smb
run_in_mount :smb do |mount_point|
mount_file = File.join(mount_point, "db_dump", console_smb_dump_file_without_ca)
assert_file_exists mount_file
DbTestCase.no_custom_attributes? console_smb_dump_file_without_ca
end
end
# --------------------------------------------- #
testing "appliance_console: SMB Database Dumps are valid" do
DbTestCase.valid_database? console_smb_dump_file,
console_split_smb_dump_file
end
# --------------------------------------------- #
original_console_s3_dump_size = nil
console_s3_dump_file = "console_full_s3_dump.tar.gz"
testing "appliance_console: S3 Database Dump" do
ApplianceConsoleRunner.dump console_s3_dump_file, :s3
s3_file = File.join("db_dump", console_s3_dump_file)
assert_file_exists s3_file
original_console_s3_dump_size = get_file_size s3_file
# Download DB to NFS share for validation later
run_in_mount(:nfs) { |mnt| S3Helper.download_to mnt, s3_file }
end
# --------------------------------------------- #
console_split_s3_dump_file = "console_split_s3_dump.tar.gz"
testing "appliance_console: Split S3 Database Dump" do
ApplianceConsoleRunner.dump console_split_s3_dump_file, :s3, "2M"
s3_file = File.join("db_dump", console_split_s3_dump_file)
assert_file_exists s3_file
assert_split_files s3_file, 2 * MEGABYTES, original_console_s3_dump_size, 300
# Download DB to NFS share for validation later
run_in_mount(:nfs) { |mnt| S3Helper.download_to mnt, s3_file }
end
# --------------------------------------------- #
testing "appliance_console: S3 Database Dumps are valid" do
DbTestCase.valid_database? console_s3_dump_file,
console_split_s3_dump_file
end
# --------------------------------------------- #
original_console_ftp_dump_size = nil
console_ftp_dump_file = "console_full_ftp_dump.tar.gz"
testing "appliance_console: FTP Database Dump" do
ApplianceConsoleRunner.dump console_ftp_dump_file, :ftp
assert_file_exists console_ftp_dump_file
original_console_ftp_dump_size = get_file_size console_ftp_dump_file
end
# --------------------------------------------- #
console_split_ftp_dump_file = "console_split_ftp_dump.tar.gz"
testing "appliance_console: Split FTP Database Dump" do
ftp_file = console_split_ftp_dump_file
ApplianceConsoleRunner.dump ftp_file, :ftp, "2M"
assert_split_files ftp_file, 2 * MEGABYTES, original_console_ftp_dump_size, 200
end
original_console_ftp_anonymous_dump_size = nil
console_ftp_anonymous_dump_file = "console_full_ftp_anonymous_dump.tar.gz"
testing "appliance_console: Anonymous FTP Database Dump" do
ApplianceConsoleRunner.dump console_ftp_anonymous_dump_file, :ftp_anonymous
assert_file_exists console_ftp_anonymous_dump_file
original_console_ftp_anonymous_dump_size = get_file_size console_ftp_anonymous_dump_file
end
# --------------------------------------------- #
console_split_ftp_anonymous_dump_file = "console_split_ftp_anonymous_dump.tar.gz"
testing "appliance_console: Anonymous Split FTP Database Dump" do
ftp_file = console_split_ftp_anonymous_dump_file
ApplianceConsoleRunner.dump ftp_file, :ftp_anonymous, "2M"
assert_split_files ftp_file, 2 * MEGABYTES, original_console_ftp_anonymous_dump_size, 300
end
# --------------------------------------------- #
testing "appliance_console: FTP Database Dumps are valid" do
DbTestCase.valid_database? console_ftp_dump_file,
console_split_ftp_dump_file,
console_ftp_anonymous_dump_file,
console_split_ftp_anonymous_dump_file
end
require 'pty'
class ApplianceConsoleRunner
attr_reader :state, :type, :file, :mode, :split, :finished
CLEAR_CODE = `clear`
SMB_CREDS = ["vagrant", "vagrant"].freeze
FTP_CREDS = SMB_CREDS
FTP_ANON = ["", ""].freeze
CONSOLE_CMD = "/usr/local/bin/appliance_console".freeze
STATE_OUTPUT_LINES = {
"Advanced Setting" => :main_menu,
"Backup Output File Name" => :io_menu,
"Dump Output File Name" => :io_menu,
"Restore Database File" => :io_menu
}.freeze
PRESS_ANY_KEY_REGEXP = /^Press any key to continue.*$/.freeze
MAIN_MENU_OPTION_REGEXPS = {
:backup => /^(?<OPTION_NUMBER>\d*)\).*Database Backup.*$/,
:dump => /^(?<OPTION_NUMBER>\d*)\).*Database Dump.*$/,
:restore => /^(?<OPTION_NUMBER>\d*)\).*Restore Database.*$/,
:quit => /^(?<OPTION_NUMBER>\d*)\).*Quit.*$/
}.freeze
IO_MENU_OPTION_REGEXPS = {
:local => /^(?<OPTION_NUMBER>\d*)\).*Local file.*$/,
:nfs => /^(?<OPTION_NUMBER>\d*)\).*Network File System.*$/,
:smb => /^(?<OPTION_NUMBER>\d*)\).*Samba.*$/,
:s3 => /^(?<OPTION_NUMBER>\d*)\).*Amazon S3.*$/,
:ftp => /^(?<OPTION_NUMBER>\d*)\).*File Transfer Protocol.*$/,
:swift => /^(?<OPTION_NUMBER>\d*)\).*OpenStack Swift.*$/,
:cancel => /^(?<OPTION_NUMBER>\d*)\).*Cancel.*$/,
}.freeze
def self.backup file, mode = :local, split = nil
new(:backup, file, mode, split).run_console
end
def self.dump file, mode = :local, split = nil
new(:dump, file, mode, split).run_console
end
def self.dump_with_no_custom_attributes file, mode = :local, split = nil
new(:dump, file, mode, split, ['custom_attributes']).run_console
end
def initialize type, file, mode, split, table_exclusions=[]
@state = nil
@input = []
@type = type
@mode = mode
@file = localize file
@split = split
@table_exclusions = table_exclusions
@finished = false
end
def run_console
status = nil
output_log = []
PTY.spawn CONSOLE_CMD do |out, stdin, pid|
cmd_tr = Process.detach(pid)
# Timeout Thread
Thread.new do
waits_left = starting_wait
while cmd_tr.alive? && waits_left > 0
waits_left -= 1
sleep 2
end
if cmd_tr.alive?
debug "----- (debug thread) -----"
debug input.backtrace
debug "--------------------------"
debug output.backtrace
debug "--------------------------"
debug "@state: #{@state.inspect}"
debug "@input: #{@input.inspect}"
debug "@type: #{@type.inspect}"
debug "@mode: #{@mode.inspect}"
debug "@file: #{@file.inspect}"
debug "@split: #{@split.inspect}"
debug "@table_exclusions: #{@table_exclusions.inspect}"
debug "@finished: #{@finished.inspect}"
debug "--------------------------"
debug "remaining output...."
debug out.read_nonblock.lines.map(&:inspect)
debug "----- (debug thread) -----"
Process.kill("KILL", cmd_tr.pid)
end
end
# Output parser thread
output = Thread.new do
while line = out.gets
debug "(output thread) #{line.inspect}"
new_input = line_parser line
@input += Array(new_input) if new_input
end
end
# Input thread
input = Thread.new do
while cmd_tr.alive?
if @input.empty?
sleep 0.5
else
sleep 1 # give the sub process a second to accept input
input_line = @input.shift
debug "(input thread) #{input_line.inspect}"
stdin.puts input_line
end
end
end
status = cmd_tr.value # effectively thread.join
end
fail "appliance_console command timed out" if status.signaled? && status.termsig == 9
fail "appliance_console ran into an error" unless status.success?
end
private
def localize filename
case mode
when :local
"/var/www/miq/vmdb/#{filename}"
when :nfs
@uri = "#{mode}://192.168.50.11:/var/#{mode}"
filename
when :smb
@uri = "#{mode}://192.168.50.11:/share"
filename
when :s3
@uri = "#{mode}://#{S3Helper.suite_s3_bucket_name}/"
filename
when :swift
@uri = "#{mode}://#{SwiftHelper.suite_swift_bucket_name}/"
filename
when :ftp, :ftp_anonymous
@uri = "ftp://192.168.50.11"
@uri += "/uploads" if mode == :ftp_anonymous
debug "(internal) uri to be passed: #{@uri.inspect}"
filename
end
end
def line_parser line
return "" if line =~ PRESS_ANY_KEY_REGEXP
case state
when :main_menu
menu_option = finished ? :quit : type
match = line.match MAIN_MENU_OPTION_REGEXPS[menu_option]
@state = nil if match
match && match[:OPTION_NUMBER]
when :io_menu
match = line.match IO_MENU_OPTION_REGEXPS[io_key]
if match
@state = nil
inject_input match[:OPTION_NUMBER]
end
else # state change check
# Some of the menu prompts come right after a clear, and so `out.gets`
# doesn't pick that up properly as a seperate line. Split will return
# the whole string anyway if there is no CLEAR_CODE, so doesn't hurt much
# to have it in here.
new_state = STATE_OUTPUT_LINES[line.split(CLEAR_CODE).last.strip]
@state = new_state if new_state
nil
end
end
def inject_input input_number
@finished = true
split_answers = split ? ["y", split] : ["n"]
exclude_tables = if @table_exclusions.empty?
["n"]
else
["y"] + @table_exclusions + [""]
end
inject = [input_number]
inject << file
inject << @uri unless mode == :local
inject += SMB_CREDS if mode == :smb
inject += aws_input if mode == :s3
inject += FTP_CREDS if mode == :ftp
inject += FTP_ANON if mode == :ftp_anonymous
inject += swift_input if mode == :swift
inject += exclude_tables if type == :dump
inject += split_answers
inject += ["n", "y"] if type == :restore && mode == :local
# ^ don't delete backup (but answer)
inject
end
def aws_input
[
ENV["AWS_REGION"],
ENV["AWS_ACCESS_KEY_ID"],
ENV["AWS_SECRET_ACCESS_KEY"]
]
end
def swift_input
[
SwiftHelper.auth_creds[0].to_s,
SwiftHelper.auth_creds[1].to_s,
SwiftHelper.region.to_s,
SwiftHelper.port.to_s,
SwiftHelper.security_protocol.to_s,
SwiftHelper.api_version.to_s,
(SwiftHelper.include_domain_id? ? SwiftHelper.domain_id : nil)
].compact
end
def starting_wait
case mode
when :s3, :ftp, :ftp_anonymous, :swift then 60
else 20
end
end
def io_key
case mode
when :ftp_anonymous then :ftp
else mode
end
end
def debug input
puts input if false
end
end
require 'singleton'
class TestConfig
include Singleton
CONFIG_METHODS = %w[
clean skip_backups skip_dumps
skip_splits skip_restore
include_s3 include_swift
restore_from_original_dir
]
CONFIG_METHODS.each do |config|
attr_accessor config
define_singleton_method config do
instance.public_send config
end
define_singleton_method "#{config}=" do |val|
instance.public_send "#{config}=", val
end
end
def self.clean?
clean
end
def self.run_test? test
run_backup?(test) && run_dump?(test) && run_split?(test) && run_s3?(test) && run_swift?(test)
end
def self.run_backup? test
!(TestConfig.skip_backups && test =~ /(evm:db:backup|Database Backup)/)
end
def self.run_dump? test
!(TestConfig.skip_dumps && test =~ /(evm:db:dump|Database Dump)/)
end
def self.run_split? test
!(TestConfig.skip_splits && test =~ /(-b 2M|: Split)/)
end
def self.run_s3? test
(TestConfig.include_s3 || !(test =~ /(_s3_|S3 Database)/))
end
def self.run_swift? test
(TestConfig.include_swift || !(test =~ /(_swift_|Swift Database)/))
end
end
# Defaults
TestConfig.clean = false
TestConfig.include_s3 = true
TestConfig.include_swift = true
TestConfig.skip_backups = false
TestConfig.skip_dumps = false
TestConfig.skip_splits = false
TestConfig.skip_restore = true
TestConfig.restore_from_original_dir = false
SHARE_IP = "192.168.50.11".freeze
MEGABYTES = 1024 * 1024
# HACK: "Enhance" File::Stat to keep track of the name we pass to it
File::Stat.prepend Module.new {
def initialize(filename)
@name = filename
super
end
def name; @name; end
}
require 'yaml'
require 'erb'
require 'shellwords'
require 'active_record'
require_relative "./smoketest_rake_helper.rb"
require_relative "./smoketest_ssh_helper.rb"
require_relative "./smoketest_validator.rb"
require_relative "./appliance_secondary_db.rb"
miq_password_relative_path = %w[
bundler gems manageiq-gems-pending-*
lib/gems/pending/util/miq-password.rb
]
require Dir[File.join Gem.dir, *miq_password_relative_path].first
default_root = File.join "", *%w[var www miq vmdb]
miq_root = if Dir.exists? File.join(*default_root)
default_root
else # assume running locally
File.expand_path File.join(__FILE__, *%w[.. .. manageiq])
end
ENV["KEY_ROOT"] = File.join miq_root, "certs"
yaml = File.read File.join(miq_root, 'config', 'database.yml')
data = YAML.load ERB.new(yaml).result
configurations = { "default" => data["production"].dup }
configurations["default"]["password"] = MiqPassword.try_decrypt(configurations["default"]["password"])
ActiveRecord::Base.configurations = configurations
class DbValidator
include TestHelper
include MountHelper
RAKE_RESTORE_PATCHES = "/vagrant/tests/appliance_rake_restore_patches.rb"
attr_reader :dbname, :restore_type, :rake_location, :split
def self.validate db
new(db).matches_origial?
end
def self.no_custom_attributes? db
!new(db).has_custom_attributes?
end
def self.default_vm_count
@default_vm_count ||= default_connection.vm_count
end
def self.default_custom_attribute_count
@default_custom_attribute_count ||= default_connection.custom_attribute_count
end
def self.default_connection
@default_connection ||= new
end
def self.defaults
@defaults ||= default_connection.counts
end
def initialize db = :default
@db = db
# Default DB doesn't need to be restored
unless @db == :default
set_restore_vars
initialize_db_configuration
reload_db
end
end
def matches_origial?
counts == self.class.defaults
end
def counts
{
:vms => vm_count,
:custom_attributes => custom_attribute_count
}
end
def vm_count
with_connection { ActiveRecord::Base.connection.select_rows("SELECT COUNT(id) FROM vms")[0][0] }
end
def custom_attribute_count
with_connection { ActiveRecord::Base.connection.select_rows("SELECT COUNT(id) FROM custom_attributes")[0][0] }
end
def has_custom_attributes?
custom_attribute_count > 0
end
private
def with_connection
ActiveRecord::Base.establish_connection @db.to_sym
# puts ActiveRecord::Base.connection_config.inspect
yield
ensure
ActiveRecord::Base.remove_connection
end
def initialize_db_configuration
unless @db == :default
new_configuration = ActiveRecord::Base.configurations['default'].dup
new_configuration["database"] = dump_database_name if @db =~ /dump/
if not split and not TestConfig.skip_restore and restore_type == "backup"
new_configuration["port"] = "5555"
else
new_configuration["host"] = "192.168.50.11"
end
ActiveRecord::Base.configurations[@db.to_s] = new_configuration
end
end
def reload_db
upload_local_db if @db =~ /local/
if @db =~ /backup/
load_database_backup
elsif @db =~ /dump/
load_database_dump
end
end
VMDB_DIR_ARRAY = %w[var www miq vmdb].unshift("").freeze
def upload_local_db
dir = @db =~ /backup/ ? "db_backup" : "db_dump"
dir = File.dirname File.join(dir, @db.split(File::SEPARATOR) - VMDB_DIR_ARRAY)
SSHHelper.with_session do |ssh|
Dir["/var/www/miq/vmdb/#{@db}*"].each do |file|
ssh.scp.upload! file, "/home/vagrant/"
end
ssh.exec! "sudo mkdir -p /var/nfs/#{dir}"
ssh.exec! "sudo mv /home/vagrant/#{File.basename @db}* /var/nfs/#{dir}"
end
end
DB_BACKUP_RESTORE_TEMPLATE = [
%Q{sudo rc-service -q postgresql stop},
%Q{sudo /bin/bash -c 'rm -rf /var/lib/postgresql/9.5/data/*'},
%Q{ls %s* | sort | sudo xargs cat | sudo tar -xz -C /var/lib/postgresql/9.5/data/},
%Q{sudo sed -i "s/^.*\\/etc\\/manageiq\\/postgresql.conf.d.*$/listen_addresses = '*'/" /var/lib/postgresql/9.5/data/postgresql.conf},
%Q{sudo cp /var/lib/postgresql/9.5/data/pg_hba.conf /var/lib/postgresql/9.5/data/pg_hba.conf.bak},
%Q{sudo /bin/sh -c "head -n 3 /var/lib/postgresql/9.5/data/pg_hba.conf.bak > /var/lib/postgresql/9.5/data/pg_hba.conf"},
%Q{sudo /bin/sh -c "echo 'host all all 192.168.50.10/0 md5' >> /var/lib/postgresql/9.5/data/pg_hba.conf"},
%Q{sudo rc-service -q postgresql start}
].join(" && ").freeze
def load_database_backup
if split || TestConfig.skip_restore
filepath = share_filepath_for :backup
SSHHelper.run_commands DB_BACKUP_RESTORE_TEMPLATE % filepath
else
ApplianceSecondaryDB.reset_db
RakeHelper.run_rake :restore, rake_location, @db,
:dbname => dbname,
:port => "5555",
:hostname => "localhost",
:username => "root",
:password => "smartvm",
:rake_cmd_args => { :require => RAKE_RESTORE_PATCHES }
end
end
DB_BACKUP_DUMP_TEMPLATE = [
%Q{sudo psql -qc 'DROP DATABASE IF EXISTS %s' postgres &> /dev/null},
%Q{sudo psql -qc 'CREATE DATABASE %s' postgres},
%Q{ls %s* | sort | sudo xargs cat | sudo pg_restore -d %s}
].join(" && ").freeze
def load_database_dump
if split || TestConfig.skip_restore
filepath = share_filepath_for :dump
SSHHelper.run_commands DB_BACKUP_DUMP_TEMPLATE % [dbname, dbname, filepath, dbname]
else
RakeHelper.run_rake :restore, rake_location, @db,
:dbname => dbname,
:hostname => SHARE_IP,
:username => "root",
:password => "smartvm"
end
end
def share_filepath_for action
filepath = ["db_#{action}", "#{@db}*"]
if @db =~ /ftp/
filepath.insert 0, FTPHelper.base_dir_for(filepath.last)
elsif @db =~ /_(nfs|local|s3|swift)_/
filepath.insert 0, *%w[var nfs]
elsif @db =~ /smb/
filepath.insert 0, *%w[var smb]
end
File.join "", *filepath
end
def dump_database_name
File.basename @db.to_s.split('.').first
end
def set_restore_vars
@dbname = dump_database_name
@rake_location, @restore_type, _, @split = parse_db_filename dbname
end
end
class DbTestCase
include TestHelper
attr_reader :backups_to_validate
def self.validate *dbs
new(*dbs).validate
end
# Similar to the above, but doesn't run inside of a `testing` block
def self.valid_database? *dbs
new(*dbs).valid_databases?
end
def self.no_custom_attributes? *dbs
new(*dbs).no_custom_attributes?
end
def initialize *dbs
@backups_to_validate = dbs
end
def validate
testing validation_message do
valid_databases?
end
end
def valid_databases?
backups_to_validate.each { |db| assert_valid_database db }
end
def no_custom_attributes?
backups_to_validate.each { |db| assert_no_custom_attributes db }
end
private
def validation_message
message = nil
backups_to_validate.each do |filename|
location, type, _ = parse_db_filename filename
new_message = "#{location} #{type}s are valid"
if message && message != new_message
raise ArgumentError, "use one backup type/location at a time"
end
message ||= new_message
end
message
end
def assert_no_custom_attributes backup_file
DbValidator.no_custom_attributes?(backup_file) || fail("custom_attributes is present in #{backup_file}")
end
def assert_valid_database backup_file
if run_db_validation? backup_file
DbValidator.validate(backup_file) || raise("invalid DB (call 'fail' below)")
end
rescue => e
fail <<-ERR.gsub(/^ {4}/, '') + e.backtrace.map { |l| " #{l}" }.join("\n")
#{backup_file} was not a valid database
Error: #{e.message}
ERR
end
def run_db_validation? db_file
!(TestConfig.skip_backups && db_file =~ /(console_)?(split|full)_(local|nfs|smb)_backup\.tar\.gz/) &&
!(TestConfig.skip_dumps && db_file =~ /(console_)?(split|full)_(local|nfs|smb)_dump\.tar\.gz/) &&
!(TestConfig.skip_splits && db_file =~ /(console_)?split_(local|nfs|smb)_(dump|backup)\.tar\.gz/) &&
(TestConfig.include_s3 || !(db_file =~ /(console_)?(split|full)_s3_(dump|backup)\.tar\.gz/))
(TestConfig.include_swift || !(db_file =~ /(console_)?(split|full)_swift_(dump|backup)\.tar\.gz/))
end
end
RakeHelper.test :backup, :local, "full_local_backup.tar.gz"
RakeHelper.test :backup_fktmp, :local, "full_local_fake_tmp_backup.tar.gz"
# RakeHelper.test :split_backup, :local, "split_local_backup.tar.gz"
RakeHelper.validate_databases "full_local_backup.tar.gz",
"full_local_fake_tmp_backup.tar.gz"
# "split_local_backup.tar.gz"
RakeHelper.test :backup, :nfs, "full_nfs_backup.tar.gz"
RakeHelper.test :backup_fktmp, :nfs, "full_nfs_fake_tmp_backup.tar.gz"
# RakeHelper.test :split_backup, :nfs, "split_nfs_backup.tar.gz"
RakeHelper.validate_databases "full_nfs_backup.tar.gz",
"full_nfs_fake_tmp_backup.tar.gz"
# "split_nfs_backup.tar.gz"
RakeHelper.test :backup, :smb, "full_smb_backup.tar.gz"
RakeHelper.test :backup_fktmp, :smb, "full_smb_fake_tmp_backup.tar.gz"
# RakeHelper.test :split_backup, :smb, "split_smb_backup.tar.gz"
RakeHelper.validate_databases "full_smb_backup.tar.gz",
"full_smb_fake_tmp_backup.tar.gz"
# "split_smb_backup.tar.gz"
RakeHelper.test :backup, :s3, "full_s3_backup.tar.gz"
RakeHelper.test :backup_fktmp, :s3, "full_s3_fake_tmp_backup.tar.gz"
# RakeHelper.test :split_backup, :s3, "split_s3_backup.tar.gz"
RakeHelper.validate_databases "full_s3_backup.tar.gz"
# "split_s3_backup.tar.gz"
RakeHelper.test :backup, :ftp, "full_ftp_backup.tar.gz"
RakeHelper.test :backup_fktmp, :ftp, "full_ftp_fake_tmp_backup.tar.gz"
# RakeHelper.test :split_backup, :ftp, "split_ftp_backup.tar.gz"
RakeHelper.test :backup, :ftp, "full_ftp_anonymous_backup.tar.gz"
RakeHelper.test :backup_fktmp, :ftp, "full_ftp_anonymous_fake_tmp_backup.tar.gz"
# RakeHelper.test :split_backup, :ftp, "split_ftp_anonymous_backup.tar.gz"
RakeHelper.validate_databases "full_ftp_backup.tar.gz",
# "split_ftp_backup.tar.gz",
"full_ftp_anonymous_backup.tar.gz",
"full_ftp_anonymous_backup.tar.gz",
"full_ftp_anonymous_fake_tmp_backup.tar.gz"
# "split_ftp_anonymous_backup.tar.gz"
RakeHelper.test :backup, :swift, "full_swift_backup.tar.gz"
RakeHelper.test :backup_fktmp, :swift, "full_swift_fake_tmp_backup.tar.gz"
# RakeHelper.test :split_backup, :swift, "split_swift_backup.tar.gz"
RakeHelper.validate_databases "full_swift_backup.tar.gz"
"full_swift_fake_tmp_backup.tar.gz"
# "split_swift_backup.tar.gz"
# --------------------------------------------- #
RakeHelper.test :dump, :local, "full_local_dump.tar.gz"
RakeHelper.test :dump_fktmp, :local, "full_local_fake_tmp_dump.tar.gz"
RakeHelper.test :split_dump, :local, "split_local_dump.tar.gz"
RakeHelper.validate_databases "full_local_dump.tar.gz",
"full_local_fake_tmp_dump.tar.gz",
"split_local_dump.tar.gz"
RakeHelper.test :dump, :nfs, "full_nfs_dump.tar.gz"
RakeHelper.test :dump_fktmp, :nfs, "full_nfs_fake_tmp_dump.tar.gz"
RakeHelper.test :split_dump, :nfs, "split_nfs_dump.tar.gz"
RakeHelper.validate_databases "full_nfs_dump.tar.gz",
"full_nfs_fake_tmp_dump.tar.gz",
"split_nfs_dump.tar.gz"
RakeHelper.test :dump, :smb, "full_smb_dump.tar.gz"
RakeHelper.test :dump_fktmp, :smb, "full_smb_fake_tmp_dump.tar.gz"
RakeHelper.test :split_dump, :smb, "split_smb_dump.tar.gz"
RakeHelper.validate_databases "full_smb_dump.tar.gz",
"full_smb_fake_tmp_dump.tar.gz",
"split_smb_dump.tar.gz"
RakeHelper.test :dump, :s3, "full_s3_dump.tar.gz"
RakeHelper.test :dump_fktmp, :s3, "full_s3_fake_tmp_dump.tar.gz"
RakeHelper.test :split_dump, :s3, "split_s3_dump.tar.gz"
RakeHelper.validate_databases "full_s3_dump.tar.gz",
"full_s3_fake_tmp_dump.tar.gz",
"split_s3_dump.tar.gz"
RakeHelper.test :dump, :ftp, "full_ftp_dump.tar.gz"
RakeHelper.test :dump_fktmp, :ftp, "full_ftp_fake_tmp_dump.tar.gz"
RakeHelper.test :split_dump, :ftp, "split_ftp_dump.tar.gz"
RakeHelper.test :dump, :ftp, "full_ftp_anonymous_dump.tar.gz"
RakeHelper.test :dump_fktmp, :ftp, "full_ftp_anonymous_fake_tmp_dump.tar.gz"
RakeHelper.test :split_dump, :ftp, "split_ftp_anonymous_dump.tar.gz"
RakeHelper.validate_databases "full_ftp_dump.tar.gz",
"full_ftp_fake_tmp_dump.tar.gz",
"split_ftp_dump.tar.gz",
"full_ftp_anonymous_dump.tar.gz",
"full_ftp_anonymous_fake_tmp_dump.tar.gz",
"split_ftp_anonymous_dump.tar.gz"
RakeHelper.test :dump, :swift, "full_swift_dump.tar.gz"
RakeHelper.test :dump_fktmp, :swift, "full_swift_fake_tmp_dump.tar.gz"
RakeHelper.test :split_dump, :swift, "split_swift_dump.tar.gz"
RakeHelper.validate_databases "full_swift_dump.tar.gz",
"full_swift_fake_tmp_dump.tar.gz",
"split_swift_dump.tar.gz"
class DotEnvFile
DOT_ENV_LINE_MATCH = /^(?<ENV_VAR>[A-Z_]+)\s*=\s*("|')?(?<ENV_VAL>[^"']*)?("|')?$/
def self.parse
File.open(File.join(File.dirname(__FILE__), ".env")) do |env_file|
env_file.each_line do |line|
next unless match = line.strip.match(DOT_ENV_LINE_MATCH)
ENV[match[:ENV_VAR]] = match[:ENV_VAL]
end
end
end
end
# load in env vars
DotEnvFile.parse
require 'net/ftp'
require_relative "./smoketest_ssh_helper.rb"
class FTPHelper
VAGRANT_CREDENTIALS = ["vagrant", "vagrant"]
ANONYMOUS_CREDENTIALS = []
CREDENTIALS = {
true => ANONYMOUS_CREDENTIALS,
false => VAGRANT_CREDENTIALS
}
FileStat = Struct.new(:name, :size) do
def <=> other
self.name <=> other.name
end
end
attr_accessor :ftp
attr_reader :anonymous
def self.with_connection filename = false, &block
if is_anonymous? filename
SSHHelper.instance_exec &block
else
begin
ftp = new filename
ftp.instance_exec &block
ensure
ftp.close
end
end
end
def self.rake_opts filename, options = {}
opts = %w[--uri ftp://192.168.50.11]
if options[:anonymous]
opts.last << "/uploads"
else
opts << %w[--uri-username --uri-password].zip(VAGRANT_CREDENTIALS)
end
opts << "--remote-file-name".freeze
opts << filename
opts += %w[-b 2M] if options[:split]
opts.join " "
end
def self.file_exist? filename
filename = full_filepath filename if is_anonymous? filename
with_connection(filename) { file_exist? filename }
end
def self.stat filename
filename = full_filepath filename if is_anonymous? filename
with_connection(filename) { stat filename }
end
def self.get_file_list glob
glob = full_filepath glob if is_anonymous? glob
with_connection(glob) { get_file_list glob }
end
def self.clear_user_directory
with_connection { clear_user_directory }
end
def self.full_filepath filename
File.join dirname_for(filename), filename
end
def self.dirname_for filename
File.join base_dir_for(filename), db_dir_for(filename)
end
def self.db_dir_for filename
filename =~ /dump/ ? "db_dump" : "db_backup"
end
def self.base_dir_for filename
if is_anonymous? filename
File.join "", *%w[var nfs ftp pub uploads]
else
File.join "", *%w[home vagrant]
end
end
def self.is_anonymous? filename_or_bool
if filename_or_bool.kind_of?(String)
filename_or_bool =~ /anonymous/
else
filename_or_bool
end
end
def initialize anonymous = false
@anonymous = !!self.class.is_anonymous?(anonymous)
connect
end
def connect
@ftp = Net::FTP.new("192.168.50.11")
@ftp.login(*CREDENTIALS[anonymous])
@ftp
end
def file_exist? filename
!get_file_list(filename).empty?
end
def stat filename
FileStat.new(filename, ftp.size(self.class.full_filepath filename))
rescue => e
fail "oh the huge manatee"
end
def get_file_list glob
ftp.chdir self.class.dirname_for(glob)
ftp.nlst(glob).map { |file| stat file }
end
def clear_user_directory
%w[db_dump db_backup].each do |dir|
next if ftp.nlst(dir).empty?
ftp.chdir dir
ftp.nlst("*").each { |file| ftp.delete(file) }
ftp.chdir ".."
ftp.rmdir dir
end
end
def close
ftp.close
end
end
require 'etc'
require 'uri'
require 'fileutils'
require 'singleton'
require_relative "./smoketest_validator.rb"
require_relative "./smoketest_db_helper.rb"
class RakeHelper
ACTION_MAP = {
:backup => [:backup, {}],
:backup_fktmp => [:backup, { :fake_tmp => true }],
:dump => [:dump, {}],
:dump_fktmp => [:dump, { :fake_tmp => true }],
:split_backup => [:backup, { :split => true }],
:split_dump => [:dump, { :split => true }],
:restore => [:restore, { :file_in_uri => true }]
}
include Singleton
def self.test type, location, file
action, opts = ACTION_MAP[type]
verbose = opts[:verbose]
debug = opts[:debug]
test_case = RakeTestCase.new file, action, location, opts
if verbose || debug
puts
puts
puts type.inspect
puts location.inspect
puts file.inspect
puts opts.inspect
puts test_case.send(:build_command)
puts
test_case.run_test if verbose
else
test_case.run_test
end
end
def self.run_rake type, location, file, rake_args
action, opts = ACTION_MAP[type]
opts[:args] = rake_args
verbose = opts[:args][:verbose]
debug = opts[:args][:debug]
rake_runner = RakeRunner.new file, action, location, opts
if verbose || debug
puts
puts
puts type.inspect
puts location.inspect
puts file.inspect
puts rake_args.inspect
puts rake_runner.send(:build_command)
puts
rake_runner.run_command if verbose
else
rake_runner.run_command
end
end
def self.validate_databases *db_backups
::DbTestCase.validate *db_backups
end
def self.backup_sizes
instance.backup_sizes
end
attr_reader :backup_sizes
def initialize
@backup_sizes = {}
end
end
class RakeRunner
DEFAULT_AUTH = %w[vagrant vagrant].freeze
REDACTED_AUTH = %w[******** ********].freeze
CMD_FAILED_PROMPT = <<-MSG.gsub(/^ /, "").freeze
Command Failed
--------------
MSG
include TestHelper
attr_reader :file, :action, :location, :opts, :rake_cmd_args, :verbose, :debug
def initialize file, action, location, opts = {}
@file = file
@action = action
@location = location
@opts = opts
@debug = @opts.fetch(:args, {}).delete(:debug)
@verbose = @opts.fetch(:args, {}).delete(:verbose)
@rake_cmd_args = @opts.fetch(:args, {}).delete(:rake_cmd_args) || {}
@redacted = false
end
def run_command
out, status = [StringIO.new, nil]
run_in_vmdb do
out_data, pipe = IO.pipe
Thread.new { IO.copy_stream out_data, out }
system(wrapped_cmd, [:out, :err] => pipe)
pipe.close
status = $?
end
status.success? || fail(CMD_FAILED_PROMPT + out.string)
end
private
def build_command
"rake #{task_name} #{task_args}"
end
def wrapped_cmd
"sudo /bin/sh -c 'source /etc/profile.d/evm.sh; bin/#{build_command}'"
end
def task_name
@task_name ||= "evm:db:#{action}:#{location == :local ? :local : :remote}"
end
def task_args options = {}
args = base_rake_cmd_args
case location
when :local then args += "--local-file #{file}"
when :nfs then args += nfs_args
when :smb then args += smb_args
when :s3 then args += s3_args
when :ftp then args += ftp_args
when :swift then args += swift_args
end
args += add_additional_args.to_s
args
end
def nfs_args
generic_remote_args :path => '/var/nfs', :no_auth => true
end
def smb_args
generic_remote_args :path => '/share'
end
def s3_args
params = {
:host => S3Helper.suite_s3_bucket_name,
:auth => @redacted ? REDACTED_AUTH : S3Helper.auth_creds
}
generic_remote_args params
end
def ftp_args
if FTPHelper.is_anonymous? file
generic_remote_args :path => '/uploads', :no_auth => true
else
generic_remote_args
end
end
def swift_args
params = {
:host => SwiftHelper.suite_host_name_and_port,
:path => SwiftHelper.suite_container_path_and_options,
:auth => @redacted ? REDACTED_AUTH : SwiftHelper.auth_creds
}
generic_remote_args params
end
def generic_remote_args params = {}
path = params[:path] || ''
host = params[:host] || SHARE_IP
user, pass = params[:auth] || DEFAULT_AUTH
uri = URI::Generic.new *URI.split("#{location}://#{host}#{path}")
uri.path << "/#{file}" if root_file_in_uri?
uri.path << "/#{namespaced_file}" if namespaced_file_in_uri?
args = %Q{--uri "#{uri}"}
args << " --uri-username #{user}" unless params[:no_auth]
args << " --uri-password #{pass}" unless params[:no_auth]
args << " --remote-file-name #{file}" unless opts[:file_in_uri]
args
end
def base_rake_cmd_args
result = args_hash_to_cmdline_string rake_cmd_args
result << " --trace" if verbose
result << " -- "
result
end
def add_additional_args
args_hash_to_cmdline_string @opts[:args] if @opts[:args]
end
def args_hash_to_cmdline_string args_hash
args_hash.map do |key, value|
if value.kind_of? Array
value.map { |v| "--#{key.to_s.tr('_', '-')} #{v}" }.join(" ")
else
"--#{key.to_s.tr('_', '-')} #{value}"
end
end.unshift(nil).join(" ")
end
def namespaced_file
return @namespaced_file if @namespaced_file
@namespaced_file = if location == :local
file
else
_, type, _ = parse_db_filename file
folder = type == "backup" ? "db_backup" : "db_dump"
File.join(folder, file)
end
end
def root_file_in_uri?
opts[:file_in_uri] && (!TestConfig.restore_from_original_dir && [:nfs, :smb].include?(location))
end
def namespaced_file_in_uri?
opts[:file_in_uri] && (TestConfig.restore_from_original_dir || ![:nfs, :smb].include?(location))
end
end
class RakeTestCase < RakeRunner
SPLIT_SIZE = 2 * MEGABYTES
RAKE_TMPDIR_PATCHES = "/vagrant/tests/appliance_tmpdir_patches.rb"
include MountHelper
include Assertions
def initialize *args
super
if @opts.delete(:fake_tmp) { false }
@rake_cmd_args[:require] ||= nil
@rake_cmd_args[:require] = Array(@rake_cmd_args[:require])
@rake_cmd_args[:require] << RAKE_TMPDIR_PATCHES
end
end
def split?
@opts[:split]
end
def print_command
@redacted = true
build_command
ensure
@redacted = false
end
def run_test
testing print_command do
run_command
check_sizes
end
end
def check_sizes
run_in_mount location do
mount_file = File.join(*[@mount_point, namespaced_file].compact)
if split?
original_size = reference_db_size
assert_split_files mount_file, SPLIT_SIZE, reference_db_size, error_margin
else
assert_file_exists mount_file
save_db_size mount_file
end
end
download_for_validation
end
private
def task_args
args = super
args += " -b 2M" if split?
args
end
def reference_db_size
reference_filename = file.sub /split/, 'full'
RakeHelper.backup_sizes[reference_filename]
end
def save_db_size filepath
RakeHelper.backup_sizes[file] = get_file_size filepath
end
def error_margin
margin_of_error_for action, location
end
def download_for_validation
case location
# Move generated files on nfs/smb mounts to root to allow restore to
# work, since it currently can't work with nested directories on mounts.
when :nfs, :smb then
run_in_mount(location) do |mnt|
source = File.join mnt, "#{namespaced_file}*"
FileUtils.cp_r Dir[source], mnt
end unless TestConfig.restore_from_original_dir
# Download DB to NFS share for validation
when :s3 then
run_in_mount(:nfs) { |mnt| S3Helper.download_to mnt, namespaced_file }
when :swift then
run_in_mount(:nfs) { |mnt| SwiftHelper.download_to mnt, namespaced_file }
end
end
end
require 'etc'
require 'tmpdir'
require 'aws-sdk'
class S3Helper
def self.client
@client ||= Aws::S3::Client.new
end
def self.suite_s3_bucket_name
@suite_s3_bucket_name ||= Dir::Tmpname.make_tmpname bucket_prefix, bucket_suffix
end
def self.helper_bucket_tag_set
[
{
:key => "appliance_console_smoketest",
:value => "true"
}
]
end
def self.create_bucket
client.create_bucket :bucket => suite_s3_bucket_name
client.put_bucket_tagging :bucket => suite_s3_bucket_name,
:tagging => { :tag_set => helper_bucket_tag_set }
end
def self.auth_creds
[ENV["AWS_ACCESS_KEY_ID"], ENV["AWS_SECRET_ACCESS_KEY"]]
end
def self.rake_opts filename, options={}
opts = ["--uri", "s3://#{suite_s3_bucket_name}"]
opts << "--uri-username"
opts << (options[:redacted] ? "********" : ENV["AWS_ACCESS_KEY_ID"])
opts << "--uri-password"
opts << (options[:redacted] ? "********" : ENV["AWS_SECRET_ACCESS_KEY"])
opts << "--remote-file-name"
opts << filename
opts.join(" ")
end
def self.to_uri filename
"s3://#{suite_s3_bucket_name}/#{filename}"
end
def self.file_exist? filename
stat filename
end
def self.stat filename
if filename.kind_of? Aws::S3::Types::Object
filename
else
get_file_list(filename).first
end
end
def self.get_file_list prefix
client.list_objects(:bucket => suite_s3_bucket_name, :prefix => prefix)
.contents
end
def self.download_to dir, filename
FileUtils.mkdir_p File.dirname(File.join(dir, filename)) # just incase
client.list_objects(:bucket => suite_s3_bucket_name, :prefix => filename)
.contents.map(&:key)
.each do |object_key|
client.get_object :bucket => suite_s3_bucket_name,
:key => object_key,
:response_target => File.join(dir, object_key)
end
end
# Deletes all buckets that have been created by this user from this lib
# (matches the generated name and tagging structure).
#
# First has to remove all objects that are within the bucket, if any, and
# then removes the bucket itself.
def self.clear_buckets
my_buckets.each do |bucket|
next if bucket == suite_s3_bucket_name # skip current bucket (just created)
# puts bucket
next unless is_smoketest_tagged?(bucket)
# puts " #{client.get_bucket_tagging(:bucket => bucket).to_h[:tag_set]}"
bucket_objects = client.list_objects(:bucket => bucket).contents
unless bucket_objects.empty?
objects_to_delete = { :bucket => bucket, :delete => { :objects => [] } }
bucket_objects.inject(objects_to_delete) do |memo, obj|
memo[:delete][:objects] << { :key => obj.key }
memo
end
# puts " #{objects_to_delete.inspect}"
client.delete_objects(objects_to_delete)
end
client.delete_bucket(:bucket => bucket)
end
end
def self.my_buckets
client.list_buckets.buckets.map(&:name)
.select { |n| n.match(/^#{bucket_prefix}.*#{bucket_suffix}$/) }
end
def self.is_smoketest_tagged? bucket
client.get_bucket_tagging(:bucket => bucket).to_h[:tag_set] == helper_bucket_tag_set
rescue Aws::S3::Errors::NoSuchTagSet
false
end
def self.bucket_prefix
@bucket_prefix ||= "#{Etc.getlogin}-"
end
def self.bucket_suffix
@bucket_suffix ||= "appliance-console-smoketest"
end
end
# Create a bucket on load
if defined? TestConfig and TestConfig.include_s3
S3Helper.create_bucket
end
# Make Aws::S3::Type::Objects sortable by key
Aws::S3::Types::Object.prepend Module.new {
def <=> other
self.key <=> other.key
end
}
require 'net/ssh'
require 'net/scp'
class SSHHelper
SHARE_HOST = "192.168.50.11".freeze
SHARE_USER = "vagrant".freeze
SHARE_KEY = "/home/vagrant/.ssh/share.id_rsa".freeze
CONFIG = {
:verify_host_key => false,
:user_known_hosts_file => File::NULL,
:keys => [SHARE_KEY]
}
LS_COMMAND = "cd %s && ls -le %s 2> /dev/null"
LS_PARSER = Regexp.new <<-'REGEXP', Regexp::EXTENDED
^
(?<FILE_PERMISSIONS>[d\-][rw\-]{9})\s+
(?<NUM_LINKS>\d+)\s+
(?<OWNER_NAME>\w+)\s+
(?<OWNER_GROUP>\w+)\s+
(?<FILE_SIZE>\d+)\s+
# WKDAY MON DAY TIMESTAMP YEAR => Mon Jan 1 00:00:00 2000
(?<MOD_TIME>\w{3}\s\w{3}\s+\d+\s\d\d:\d\d:\d\d\s\d{4})\s+
(?<FILE_NAME>.*)
$
REGEXP
FileStat = Struct.new(:name, :size) do
def <=> other
self.name <=> other.name
end
end
def self.stat filename
get_file_list(filename).first
end
def self.file_exist? filename
!get_file_list(filename).empty?
end
SSH_CMD_FAIL_MSG = "the following ssh cmd failed:\n\n `%s`\n\n%s"
def self.run_commands *cmds
with_session do |ssh|
cmds.each do |cmd|
result = ssh.exec! cmd
fail SSH_CMD_FAIL_MSG % [cmd, result] if result.exitstatus != 0
end
end
end
def self.get_file_list glob
with_session do |ssh|
dir = File.dirname glob
lsglob = File.basename glob
lsglob = "#{lsglob}*" if lsglob[-1] == "*"
cmd = LS_COMMAND % [dir, lsglob]
result = ssh.exec! cmd
if result.exitstatus == 0
result.chomp.each_line.map do |line|
match = line.match(LS_PARSER)
FileStat.new match[:FILE_NAME], match[:FILE_SIZE].to_i
end
else
[]
end
end
end
def self.with_session &block
Net::SSH.start(SHARE_HOST, SHARE_USER, CONFIG.dup) do |ssh|
yield ssh
end
end
end
require 'etc'
require 'tmpdir'
require 'fog/openstack'
require_relative 'smoketest_constants'
class SwiftHelper
SWIFT_SHARE_HOST = "#{SHARE_IP}:8080".freeze
SWIFT_SHARE_TENANT = "test".freeze
SWIFT_SHARE_USERNAME = "tester".freeze
SWIFT_SHARE_PASSWORD = "testing".freeze
SWIFT_SHARE_REGION = "1".freeze
SWIFT_SHARE_SECURITY_PROTOCOL = "non-ssl".freeze
SWIFT_SHARE_API_VERSION = "v3".freeze
SWIFT_SHARE_DOMAIN_ID = "default".freeze
# Debugging helpers
class << self
attr_writer :container_prefix
end
def self.client
@client ||= Fog::Storage::OpenStack.new(fog_params)
end
def self.suite_swift_container_name
@suite_swift_container_name ||= Dir::Tmpname.make_tmpname container_prefix,
container_suffix
end
def self.create_container
client.put_container suite_swift_container_name
end
def self.auth_creds
[
ENV["SWIFT_USERNAME"] || SWIFT_SHARE_USERNAME,
ENV["SWIFT_PASSWORD"] || SWIFT_SHARE_PASSWORD
]
end
def self.file_exist? filename
stat filename
end
def self.stat filename
if filename.kind_of? Fog::Storage::OpenStack::File
filename
else
get_file_list(filename).first
end
end
def self.get_file_list prefix
client.directories
.get(suite_swift_container_name, :prefix => prefix)
.files
end
def self.download_to dir, filename
# Yes, this might seem stupid as why we are doing a File.join to then do a
# `dirname` on that when we have the `dir` already.
#
# Well, the `filename` most likely will be namespaced with `db_dump` or
# `db_backup`, so we want to make sure that is included in the `mkdir -p`.
FileUtils.mkdir_p File.dirname(File.join(dir, filename))
get_file_list(filename).each do |object|
File.open File.join(dir, object.key), "wb" do |target_file|
client.get_object(object.directory.key, object.key) do |chunk, _total, _left|
target_file << chunk
end
end
end
end
# Deletes all containers that have been created by this user from this lib
#
# By usinge client.delete_multiple_objects, we can delete the objects and the
# container itself in a single request via the `bulk-delete` functionality.
def self.clear_containers
my_containers.each do |container|
next if container.key == suite_swift_container_name # skip current container (just created)
objects_to_delete = container.files.map { |f| "#{container.key}/#{f.key}" }
objects_to_delete << container.key
client.delete_multiple_objects(nil, objects_to_delete)
end
end
# Fetches all the containers made by this lib.
#
# Filters both by just the prefix (API filter) and then again in ruby just to
# make sure what we are fetching by is as accurate as possible.
def self.my_containers
client.directories.all(:prefix => container_prefix)
.select { |c| c.key.match(/^#{container_prefix}.*#{container_suffix}$/) }
end
def self.container_prefix
@container_prefix ||= "#{Etc.getlogin}-"
end
def self.container_suffix
@container_suffix ||= "appliance-console-smoketest".freeze
end
def self.suite_container_path_and_options
return @suite_container_path_and_options if defined? @suite_container_path_and_options
@suite_container_path_and_options = "/#{suite_swift_container_name}"
params = []
params << "security_protocol=#{security_protocol}" if security_protocol
params << "api_version=#{api_version}" if api_version
params << "domain_id=#{domain_id}" if include_domain_id?
params << "region=#{region}" if region
@suite_container_path_and_options << "?#{params.join('&')}" unless params.empty?
@suite_container_path_and_options
end
def self.fog_params
{
:openstack_auth_url => auth_url,
:openstack_username => auth_creds.first,
:openstack_api_key => auth_creds.last,
:openstack_project_domain_id => domain_id,
:openstack_user_domain_id => domain_id,
:openstack_region => region,
:connection_options => { :debug_request => true }
}
end
def self.auth_url
auth_url = URI::Generic.build(
:scheme => security_protocol == 'non-ssl' ? "http" : "https",
:host => host,
:port => port.to_i,
:path => "/#{api_version}/auth/tokens"
).to_s
end
def self.suite_host_name_and_port
return @suite_host_name_and_port if defined? @suite_host_name_and_port
@suite_host_name_and_port = ENV["SWIFT_HOST"] ||
SWIFT_SHARE_HOST
@host, @port = @suite_host_name_and_port.split(":")
@suite_host_name_and_port
end
def self.host
@host ||= begin
suite_host_name_and_port
@host
end
end
def self.port
@port ||= begin
suite_host_name_and_port
@port
end
end
def self.security_protocol
@security_protocol ||= if ENV["SWIFT_SECURITY_PROTOCOL"]
nil_if_empty ENV["SWIFT_SECURITY_PROTOCOL"]
else
SWIFT_SHARE_SECURITY_PROTOCOL
end
end
def self.api_version
@api_version ||= if ENV["SWIFT_API_VERSION"]
nil_if_empty ENV["SWIFT_API_VERSION"]
else
SWIFT_SHARE_API_VERSION
end
end
def self.domain_id
@domain_id ||= if ENV["SWIFT_DOMAIN_ID"]
nil_if_empty ENV["SWIFT_DOMAIN_ID"]
else
SWIFT_SHARE_DOMAIN_ID
end
end
def self.region
@region ||= if ENV["SWIFT_REGION"]
nil_if_empty ENV["SWIFT_REGION"]
else
SWIFT_SHARE_REGION
end
end
def self.tenant
@tenant ||= if ENV["SWIFT_TENANT"]
nil_if_empty ENV["SWIFT_TENANT"]
else
SWIFT_SHARE_TENANT
end
end
def self.nil_if_empty var
var.strip.empty? ? nil : var
end
def self.include_domain_id?
domain_id && api_version && api_version == "v3"
end
end
# Create a container on load
if defined? TestConfig and TestConfig.include_swift
SwiftHelper.create_container
end
# Make Fog::Storage::OpenStack::File sortable by key
# this next line just allows you to run this file and being able to comment out
# the SwiftHelper.create_container line in case you just need to test some
# things without automatically creating a container.
Fog::Storage::OpenStack.setup_requirements
Fog::Storage::OpenStack::File.prepend Module.new {
def <=> other
self.key <=> other.key
end
def size; content_length; end
}
require 'tmpdir'
require 'singleton'
require_relative "./smoketest_constants.rb"
require_relative "./smoketest_env_helper.rb"
require_relative "./smoketest_validator.rb"
require_relative "./smoketest_s3_helper.rb"
require_relative "./smoketest_ftp_helper.rb"
require_relative "./smoketest_swift_helper.rb"
require_relative "./smoketest_rake_helper.rb"
require_relative "./smoketest_appliance_console_helper.rb"
class TestErrors
include Singleton
def self.add description, error
instance.add_error description, error
end
def self.print
instance.print_errors
end
def initialize
@errors = []
end
def add_error description, error
@errors << [description, error]
end
def print_errors
return if @errors.empty?
puts ""
puts "\e[31mThere were some failures...\e[0m"
puts ""
@errors.each_with_index do |(desc, error), i|
error_message = error.message.lines.first +
error.message.lines[1..-1].map { |l| " " * 8 + l }.join("")
puts <<-ERROR.gsub(/^ {4}/, '').prepend("\e[31m").concat("\e[0m")
#{i+1}. #{desc}
#{error_message}
ERROR
end
end
end
extend TestHelper
extend MountHelper
extend Assertions
at_exit { TestErrors.print }
def clean
clean! if TestConfig.clean?
end
def clean!
require 'fileutils'
run_in_vmdb do
File.unlink *Dir["*.tar.gz*"]
FileUtils.rm_rf "tmp/subdir"
end
%i[nfs smb].each do |type|
run_in_mount type do |mnt_dir|
FileUtils.rm_rf File.join(mnt_dir, "db_backup")
FileUtils.rm_rf File.join(mnt_dir, "db_dump")
# Quicker to just do a `rm -rf` from the mount most likely
uploads_dir = [mnt_dir, "ftp", "pub", "uploads"]
FileUtils.rm_rf File.join(*(uploads_dir + ["db_backup"])) if type == :nfs
FileUtils.rm_rf File.join(*(uploads_dir + ["db_dump"])) if type == :nfs
end
end
FTPHelper.clear_user_directory
S3Helper.clear_buckets if TestConfig.include_s3
SwiftHelper.clear_containers if TestConfig.include_swift
end
module TestHelper
def testing desc
if TestConfig.run_test? desc
print "Testing \e[1m#{desc}\e[0m... "
yield
puts "\e[32m✓\e[0m"
end
rescue RuntimeError => e
puts "\e[31mx\e[0m"
TestErrors.add desc, e
end
def run_in_vmdb
Dir.chdir("/var/www/miq/vmdb") { yield }
end
def parse_db_filename filename
parts = filename.split("_") - ["console"]
split = parts.first == "split"
location = parts[1].to_sym
type = parts.last.split(".").first
console = filename.start_with?("console")
[location, type, console, split]
end
end
module MountHelper
MOUNT_TYPE_STRINGS = {
:nfs => "sudo mount -t nfs '192.168.50.11:/var/nfs' %s",
:smb => "sudo mount -t cifs '//192.168.50.11/share' %s -o rw,username=vagrant,password=vagrant"
}
def run_in_mount mount_type
if mount_cmd = MOUNT_TYPE_STRINGS[mount_type]
@mount_point = Dir.mktmpdir "miq_"
system mount_cmd % @mount_point
end
yield @mount_point
ensure
if @mount_point && Dir.exist?(@mount_point)
system "sudo umount #{@mount_point}"
Dir.rmdir @mount_point
end
@mount_point = nil
end
end
module Assertions
def assert_file_exists filename
if filename =~ /_s3_/
S3Helper.file_exist? filename
elsif filename =~ /_ftp_/
file = File.basename filename
FTPHelper.file_exist? file
elsif filename =~ /_swift_/
SwiftHelper.file_exist? file
else
run_in_vmdb do
File.exist? filename
end
end || fail("#{filename} not found!")
end
# With this, when testing a backup (versus a dump), since the postgres process
# is still running, this can change the filesize (and usually does) within the
# 5 seconds of running the "original" test and the "split" test.
#
# Usually isn't more than 80 bytes, but keeping a decent margin for error just
# in case, and the output is pretty verbose when it fails.
def assert_split_totals split_files, expected_total, margin
actual_total = split_files.inject(0) { |sum, file| sum += file.size }
actual_total == expected_total || # totals are the same
(actual_total > expected_total && (actual_total - expected_total) < margin) || # totals within 200 bytes
fail_for_combined_total(split_files, expected_total)
end
def assert_correct_split_sizes file_list, split_amount
return if file_list.all? { |file| file.size == split_amount }
err_msg = ["Split files have the wrong sizes!"]
err_msg += file_list.map { |file| " #{file.name}: #{file.size} (expected #{split_amount})" }
fail err_msg.join("\n")
end
def assert_split_files split_base_uri, split_amount, total_size, margin_of_error=0
get_file_list(split_base_uri).tap do |file_list|
file_list.count > 0 || fail("File list for #{split_base_uri}* is empty!")
assert_split_totals file_list, total_size, margin_of_error
assert_correct_split_sizes file_list.sort[0..-2], split_amount
end
end
def margin_of_error_for action, location
if action == :dump
0
elsif location == :s3 or location == :swift
300
else
200
end
end
def get_file_list(split_base_uri)
if split_base_uri =~ /_s3_/
S3Helper.get_file_list(split_base_uri)
elsif split_base_uri =~ /_ftp_/
FTPHelper.get_file_list("#{File.basename split_base_uri}.*")
elsif split_base_uri =~ /_swift_/
SwiftHelper.get_file_list(split_base_uri)
else
# Gets files for mounted dirs or one that have been saved locally.
#
# Either we save them to vmdb root, in which we are just using relative
# paths, or this is for a mount, which we have already joined to get an absolute path in the tests.
#
# just how I have written them... #dealwithit
#
# Also, wrap these in `File` objects so we can `.size` them directly, and
# calling out multiple times to `get_file_size` isn't necessary. This is
# assuming in the specs that all of the tests are going to re-download or
# mount the files we are asking for, but since we need to do this for
# validating the DBs anyway, this isn't a problem.
run_in_vmdb do
Dir["#{split_base_uri}.*"].map { |filename| File::Stat.new(filename) }
end
end
end
def get_file_size filename
if filename =~ /_s3_/
S3Helper.stat(filename).size
elsif filename =~ /_ftp_/
FTPHelper.stat(File.basename filename).size
elsif filename =~ /_swift_/
SwiftHelper.stat(filename).size
else
run_in_vmdb { File.stat(filename).size }
end
end
def fail_for_combined_total file_list, expected_total
err_msg = ["Split files don't add up to original!"]
err_msg += file_list.map { |file| " #{file.name}: #{file.size}" }
err_msg << "Expected Total: #{expected_total}"
err_msg << "Actual Total: #{file_list.map(&:size).reduce(0, :+)}"
fail err_msg.join("\n")
end
end
MIQ_GH_URL_BASE=https://raw.githubusercontent.com/ManageIQ
APPLIANCE_AWESOME_SPAWN_DIR=$(ls -d /usr/local/lib/ruby/gems/2.3.0/gems/awesome_spawn-* | tail -n 1)
APPLIANCE_GEMS_PENDING_DIR=$(ls -d /usr/local/lib/ruby/gems/2.3.0/bundler/gems/manageiq-gems-pending-* | tail -n 1)
APPLIANCE_CONSOLE_DIR=$(ls -d /usr/local/lib/ruby/gems/2.3.0/gems/manageiq-appliance_console-* | tail -n 1)
###### undo awesome_spawn changes
curl --silent "${MIQ_GH_URL_BASE}/awesome_spawn/master/lib/awesome_spawn.rb" > $APPLIANCE_AWESOME_SPAWN_DIR/lib/awesome_spawn.rb
rm -rf $APPLIANCE_AWESOME_SPAWN_DIR/lib/core_ext
###### undo manageiq changes
MIQ_URL="${MIQ_GH_URL_BASE}/manageiq/master/"
curl --silent "${MIQ_URL}/lib/evm_database_ops.rb" > /var/www/miq/vmdb/lib/evm_database_ops.rb
curl --silent "${MIQ_URL}/lib/tasks/evm_dba.rake" > /var/www/miq/vmdb/lib/tasks/evm_dba.rake
###### undo manageiq-gems-pending changes
GEMS_PENDING_URL="${MIQ_GH_URL_BASE}/manageiq-gems-pending/gaprindashvili/lib/gems/pending/util"
curl --silent "${GEMS_PENDING_URL}/postgres_admin.rb" > $APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/postgres_admin.rb
curl --silent "${GEMS_PENDING_URL}/mount/miq_generic_mount_session.rb" > $APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/mount/miq_generic_mount_session.rb
curl --silent "${GEMS_PENDING_URL}/mount/miq_glusterfs_session.rb" > $APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/mount/miq_glusterfs_session.rb
curl --silent "${GEMS_PENDING_URL}/mount/miq_nfs_session.rb" > $APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/mount/miq_nfs_session.rb
curl --silent "${GEMS_PENDING_URL}/mount/miq_smb_session.rb" > $APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/mount/miq_smb_session.rb
rm -rf $APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/object_storage
rm -rf $APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/miq_ftp_lib.rb
rm -rf $APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/miq_file_storage.rb
rm -rf $APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/miq_object_storage.rb
rm -rf $APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/mount/miq_local_mount_session.rb
rm -rf $APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/extensions/aws-sdk
###### undo manageiq-appliance_console changes
CONSOLE_URL="${MIQ_GH_URL_BASE}/manageiq-appliance_console/v2.0.3"
curl --silent "${CONSOLE_URL}/bin/appliance_console" > $APPLIANCE_CONSOLE_DIR/bin/appliance_console
curl --silent "${CONSOLE_URL}/lib/manageiq-appliance_console.rb" > $APPLIANCE_CONSOLE_DIR/lib/manageiq-appliance_console.rb
curl --silent "${CONSOLE_URL}/lib/manageiq/appliance_console/database_admin.rb" > $APPLIANCE_CONSOLE_DIR/lib/manageiq/appliance_console/database_admin.rb
curl --silent "${CONSOLE_URL}/lib/manageiq/appliance_console/database_configuration.rb" > $APPLIANCE_CONSOLE_DIR/lib/manageiq/appliance_console/database_configuration.rb
curl --silent "${CONSOLE_URL}/lib/manageiq/appliance_console/i18n.rb" > $APPLIANCE_CONSOLE_DIR/lib/manageiq/appliance_console/i18n.rb
curl --silent "${CONSOLE_URL}/lib/manageiq/appliance_console/prompts.rb" > $APPLIANCE_CONSOLE_DIR/lib/manageiq/appliance_console/prompts.rb
curl --silent "${CONSOLE_URL}/locales/appliance/en.yml" > $APPLIANCE_CONSOLE_DIR/locales/appliance/en.yml
APPLIANCE_AWESOME_SPAWN_DIR=$(ls -d /usr/local/lib/ruby/gems/2.4.0/gems/awesome_spawn-* | tail -n 1)
APPLIANCE_GEMS_PENDING_DIR=$(ls -d /usr/local/lib/ruby/gems/2.4.0/bundler/gems/manageiq-gems-pending-* | tail -n 1)
APPLIANCE_CONSOLE_DIR=$(ls -d /usr/local/lib/ruby/gems/2.4.0/gems/manageiq-appliance_console-* | tail -n 1)
###### awesome_spawn changes
cp /vagrant/awesome_spawn/lib/awesome_spawn.rb \
$APPLIANCE_AWESOME_SPAWN_DIR/lib/awesome_spawn.rb
mkdir -p $APPLIANCE_AWESOME_SPAWN_DIR/lib/core_ext
cp /vagrant/awesome_spawn/lib/core_ext/* \
$APPLIANCE_AWESOME_SPAWN_DIR/lib/core_ext/
###### manageiq changes
cp /vagrant/manageiq/lib/evm_database_ops.rb \
/var/www/miq/vmdb/lib/evm_database_ops.rb
cp /vagrant/manageiq/lib/tasks/evm_dba.rake \
/var/www/miq/vmdb/lib/tasks/evm_dba.rake
###### manageiq-gems-pending "new" changes
cp /vagrant/manageiq-gems-pending/lib/gems/pending/util/postgres_admin.rb \
$APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/postgres_admin.rb
cp /vagrant/manageiq-gems-pending/lib/gems/pending/util/miq_ftp_lib.rb \
$APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/miq_ftp_lib.rb
cp /vagrant/manageiq-gems-pending/lib/gems/pending/util/miq_file_storage.rb \
$APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/miq_file_storage.rb
cp /vagrant/manageiq-gems-pending/lib/gems/pending/util/miq_object_storage.rb \
$APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/miq_object_storage.rb
cp /vagrant/manageiq-gems-pending/lib/gems/pending/util/mount/miq_generic_mount_session.rb \
$APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/mount/miq_generic_mount_session.rb
cp /vagrant/manageiq-gems-pending/lib/gems/pending/util/mount/miq_local_mount_session.rb \
$APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/mount/miq_local_mount_session.rb
cp /vagrant/manageiq-gems-pending/lib/gems/pending/util/mount/miq_glusterfs_session.rb \
$APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/mount/miq_glusterfs_session.rb
cp /vagrant/manageiq-gems-pending/lib/gems/pending/util/mount/miq_nfs_session.rb \
$APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/mount/miq_nfs_session.rb
cp /vagrant/manageiq-gems-pending/lib/gems/pending/util/mount/miq_smb_session.rb \
$APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/mount/miq_smb_session.rb
mkdir -p $APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/object_storage
cp /vagrant/manageiq-gems-pending/lib/gems/pending/util/object_storage/miq_s3_storage.rb \
$APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/object_storage/miq_s3_storage.rb
cp /vagrant/manageiq-gems-pending/lib/gems/pending/util/object_storage/miq_swift_storage.rb \
$APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/object_storage/miq_swift_storage.rb
cp /vagrant/manageiq-gems-pending/lib/gems/pending/util/object_storage/miq_ftp_storage.rb \
$APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/object_storage/miq_ftp_storage.rb
# aws-sdk monkey patch
mkdir -p $APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/extensions/aws-sdk
cp /vagrant/manageiq-gems-pending/lib/gems/pending/util/extensions/aws-sdk/s3_upload_stream_patch.rb \
$APPLIANCE_GEMS_PENDING_DIR/lib/gems/pending/util/extensions/aws-sdk/s3_upload_stream_patch.rb
###### manageiq-appliance_console changes
cp /vagrant/manageiq-appliance_console/bin/appliance_console \
$APPLIANCE_CONSOLE_DIR/bin/appliance_console
cp /vagrant/manageiq-appliance_console/lib/manageiq-appliance_console.rb \
$APPLIANCE_CONSOLE_DIR/lib/manageiq-appliance_console.rb
cp /vagrant/manageiq-appliance_console/lib/manageiq/appliance_console/database_admin.rb \
$APPLIANCE_CONSOLE_DIR/lib/manageiq/appliance_console/database_admin.rb
cp /vagrant/manageiq-appliance_console/lib/manageiq/appliance_console/database_configuration.rb \
$APPLIANCE_CONSOLE_DIR/lib/manageiq/appliance_console/database_configuration.rb
cp /vagrant/manageiq-appliance_console/lib/manageiq/appliance_console/i18n.rb \
$APPLIANCE_CONSOLE_DIR/lib/manageiq/appliance_console/i18n.rb
cp /vagrant/manageiq-appliance_console/lib/manageiq/appliance_console/prompts.rb \
$APPLIANCE_CONSOLE_DIR/lib/manageiq/appliance_console/prompts.rb
cp /vagrant/manageiq-appliance_console/locales/appliance/en.yml \
$APPLIANCE_CONSOLE_DIR/locales/appliance/en.yml
# -*- mode: ruby -*-
# vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
# Setup keys for inter-VM communitcation to the :share VM
_, priv_key, pub_key = [nil, nil, nil]
unless File.exist? "share.id_rsa" and File.exist? "share.id_rsa.pub"
require "vagrant/util/keypair"
_, priv_key, pub_key = Vagrant::Util::Keypair.create
File.write "share.id_rsa", priv_key
File.write "share.id_rsa.pub", pub_key
end
Vagrant.configure("2") do |config|
config.vm.provider "virtualbox"
# Appliance VM for doing tests on
config.vm.define :appliance do |miq|
miq.vm.box = "manageiq/hammer"
miq.vm.network :private_network, :ip => '192.168.50.10'
miq.vm.synced_folder ".", "/vagrant", disabled: true
rsync_opts = {
:type => :rsync,
:rsync__exclude => [".git/", "tmp/"]
}
miq.vm.synced_folder "../awesome_spawn", "/vagrant/awesome_spawn", rsync_opts
miq.vm.synced_folder "../manageiq/lib", "/vagrant/manageiq/lib", :type => :rsync
miq.vm.synced_folder "../manageiq-gems-pending", "/vagrant/manageiq-gems-pending", rsync_opts
miq.vm.synced_folder "../manageiq-performance", "/vagrant/manageiq-performance", rsync_opts
miq.vm.synced_folder "../manageiq-appliance_console", "/vagrant/manageiq-appliance_console", rsync_opts
miq.vm.synced_folder "./", "/vagrant/tests", rsync_opts
# Create a small disk we can add to the VM that we can stub in for `/tmp`
#
# Only make it 3MB in size (smaller than DB dump size after FS overhead)
fake_tmp_fstab_entry = [
"/dev/loop0".rjust(41),
"/fake_tmp".rjust(37),
"ext4".ljust(16),
"loop".ljust(15),
"0".ljust(8),
"0"
].join " "
miq.vm.provision "mount_fake_tmp", :type => "shell", :inline => <<-MOUNT_DISK
dd if=/dev/zero of=/tmp/fake_tmp_fs bs=3M count=1
losetup /dev/loop0 /tmp/fake_tmp_fs
mkfs.ext4 /dev/loop0
mkdir /fake_tmp
echo "#{fake_tmp_fstab_entry}" >> /etc/fstab
mount /dev/loop0
chmod -R 777 /fake_tmp
chmod +t /fake_tmp
MOUNT_DISK
###### copy and update ssh key permissions
miq.vm.provision "ssh_key", :type => "shell", :inline => <<-SSH_KEY
cp /vagrant/tests/share.id_rsa /home/vagrant/.ssh/
chmod 0700 /home/vagrant/.ssh/share.id_rsa
chown vagrant:vagrant /home/vagrant/.ssh/share.id_rsa
SSH_KEY
# Skip for now since we seem to need this for allowing seeding
miq.vm.provision "stop_appliance", :type => "shell", :run => "never",
:inline => "systemctl stop evmserverd"
# Now not needed since we us as hammer appliance, and sync kinda just
# messes stuff up
miq.vm.provision "sync", :type => "shell", :run => "never",
:path => "vagrant_provision_appliance_sync.sh"
miq.vm.provision "reset", :type => "shell", :run => "never",
:path => "vagrant_provision_appliance_reset.sh"
miq.vm.provision "seed", :type => "shell", :inline => <<-SEED
SEED_SCRIPT_URL=https://gist.githubusercontent.com/NickLaMuro/87dddcfbd549b03099f8e55f632b2b57/raw/f0f2583bb453366304d61e41f7db18091d7e7d57/bz_1592480_db_replication_script.rb
SEED_SCRIPT=/var/www/miq/vmdb/tmp/bz_1592480_db_replication_script.rb
echo "check_file = File.join(File.dirname(__FILE__), 'db_seeding_done')" > $SEED_SCRIPT
echo "exit if File.exist?(check_file)" >> $SEED_SCRIPT
echo "" >> $SEED_SCRIPT
echo "system 'sudo systemctl start evmserverd'" >> $SEED_SCRIPT
echo "while User.count < 1" >> $SEED_SCRIPT
echo " sleep 5 # wait for seeing on the appliance to happen" >> $SEED_SCRIPT
echo "end" >> $SEED_SCRIPT
echo "" >> $SEED_SCRIPT
curl --silent $SEED_SCRIPT_URL >> $SEED_SCRIPT
echo "" >> $SEED_SCRIPT
echo "" >> $SEED_SCRIPT
echo "system 'sudo systemctl stop evmserverd'" >> $SEED_SCRIPT
echo "system 'sudo systemctl disable evmserverd'" >> $SEED_SCRIPT
echo "File.write(check_file, '')" >> $SEED_SCRIPT
echo "" >> $SEED_SCRIPT
cd /var/www/miq/vmdb
source /etc/profile.d/evm.sh
bin/rails r $SEED_SCRIPT
SEED
end
# External vm for hosting nfs and samba mounts
config.vm.define :share do |share|
share.vm.box = "maier/alpine-3.6-x86_64"
share.vm.guest = :tinycore
share.vm.synced_folder ".", "/vagrant", disabled: true
share.vm.network :private_network, :ip => '192.168.50.11'
share.vm.provision "file", :source => "./share.id_rsa.pub", :destination => "$HOME/share.id_rsa.pub"
share.vm.provision "bootstrap", :type => "shell",
:inline => <<-BOOTSTRAP.gsub(/ {8}/, '')
cat share.id_rsa.pub >> .ssh/authorized_keys
apk update
apk add openssl-dev gcc memcached sqlite-libs xfsprogs git build-base \
libffi-dev libxml2-dev libxml2 libxslt-dev autoconf automake libtool \
linux-headers rsync rsyslog python-dev py2-pip
echo "http://dl-cdn.alpinelinux.org/alpine/v3.4/main" >> /etc/apk/repositories
apk update
apk add nfs-utils samba samba-common-tools \
"postgresql<9.6" "postgresql-client<9.6" "python3-dev<3.6" "python3<3.6" vsftpd
# ========== [Setup Swift User] ==========
adduser -D swift
# ========== [Setup Swift Dirs] ==========
cat << EOF | xargs -I {} /bin/sh -c "mkdir -p {} && chown swift:swift {}"
/opt/swift
/var/run/swift
/etc/swift
/etc/swift/account-server
/etc/swift/container-server
/etc/swift/object-server
EOF
# ========== [Clone Swift Source] ==========
git clone --depth 1 -b 2.19.0 git://github.com/openstack/swift.git /opt/swift/swift
git clone --depth 1 -b 1.5.0 git://github.com/openstack/liberasurecode.git /opt/swift/liberasurecode
git clone --depth 1 -b 1.5.0 git://github.com/openstack/pyeclib.git /opt/swift/pyeclib
git clone --depth 1 -b 14.0.0 git://github.com/openstack/keystone.git /opt/keystone
# ========== [Install Source] ==========
su -c 'cd /opt/swift/liberasurecode && ./autogen.sh && ./configure && make && make install && ldconfig'
su -c 'cd /opt/swift/pyeclib && pip install -e .'
su -c 'cd /opt/swift/swift && pip install -e .[kmip_keymaster]'
# ========== [Setup Swift Data] ==========
mkdir -p /srv /mnt/sdb1
truncate -s 250M /srv/swift-disk
(blkid | grep /srv/swift-disk | grep -q 'TYPE="xfs"') || mkfs.xfs /srv/swift-disk
grep swift-disk /etc/fstab || echo '/srv/swift-disk /mnt/sdb1 xfs loop,noatime,nodiratime,nobarrier,logbufs=8 0 0' >> /etc/fstab
mountpoint -q /mnt/sdb1 || mount /mnt/sdb1
for x in 1 2 3 4; do
mkdir -p /mnt/sdb1/$x
ln -s /mnt/sdb1/$x /srv/$x
mkdir -p /srv/$x/node/sdb$x /srv/$x/node/sdb$((x + 4))
chown swift:swift /mnt/sdb1/*
chown swift:swift -R /srv/$x/
done
# ========== [rsyncd Swift Conf] ==========
cat << EOF > /etc/rsyncd.conf
uid = swift
gid = swift
log file = /var/log/rsyncd.log
pid file = /var/run/rsyncd.pid
address = 0.0.0.0
[account6012]
max connections = 25
path = /srv/1/node/
read only = false
lock file = /var/lock/account6012.lock
[account6022]
max connections = 25
path = /srv/2/node/
read only = false
lock file = /var/lock/account6022.lock
[account6032]
max connections = 25
path = /srv/3/node/
read only = false
lock file = /var/lock/account6032.lock
[account6042]
max connections = 25
path = /srv/4/node/
read only = false
lock file = /var/lock/account6042.lock
[container6011]
max connections = 25
path = /srv/1/node/
read only = false
lock file = /var/lock/container6011.lock
[container6021]
max connections = 25
path = /srv/2/node/
read only = false
lock file = /var/lock/container6021.lock
[container6031]
max connections = 25
path = /srv/3/node/
read only = false
lock file = /var/lock/container6031.lock
[container6041]
max connections = 25
path = /srv/4/node/
read only = false
lock file = /var/lock/container6041.lock
[object6010]
max connections = 25
path = /srv/1/node/
read only = false
lock file = /var/lock/object6010.lock
[object6020]
max connections = 25
path = /srv/2/node/
read only = false
lock file = /var/lock/object6020.lock
[object6030]
max connections = 25
path = /srv/3/node/
read only = false
lock file = /var/lock/object6030.lock
[object6040]
max connections = 25
path = /srv/4/node/
read only = false
lock file = /var/lock/object6040.lock
EOF
sed -e 's/--no-detach //' \
-e 's/cfgfile$/cfgfile"/' \
-e '/command_background/d' \
-e '/RSYNC_OPTS/d' \
-i /etc/init.d/rsyncd
rc-update add rsyncd
rc-service rsyncd start
# ========== [Run memcached] ==========
rc-update add memcached
rc-service memcached start
# ========== [Setup Swift Conf] ==========
cat << SWIFT_CONF > /etc/swift/swift.conf
[swift-hash]
# random unique strings that can never change (DO NOT LOSE)
# Use only printable chars (python -c "import string; print(string.printable)")
swift_hash_path_prefix = changeme
swift_hash_path_suffix = changeme
[storage-policy:0]
name = gold
policy_type = replication
default = yes
[storage-policy:1]
name = silver
policy_type = replication
[storage-policy:2]
name = ec42
policy_type = erasure_coding
ec_type = liberasurecode_rs_vand
ec_num_data_fragments = 4
ec_num_parity_fragments = 2
SWIFT_CONF
# ---------- [Proxy Server] ----------
cat << SWIFT_PROXY_SERVER_CONF > /etc/swift/proxy-server.conf
[DEFAULT]
bind_port = 8080
workers = 1
user = swift
log_facility = LOG_LOCAL1
eventlet_debug = true
[pipeline:main]
# Yes, proxy-logging appears twice. This is so that
# middleware-originated requests get logged too.
pipeline = catch_errors gatekeeper healthcheck proxy-logging cache listing_formats bulk tempurl ratelimit crossdomain container_sync tempauth staticweb copy container-quotas account-quotas slo dlo versioned_writes symlink proxy-logging proxy-server
[filter:catch_errors]
use = egg:swift#catch_errors
[filter:healthcheck]
use = egg:swift#healthcheck
[filter:proxy-logging]
use = egg:swift#proxy_logging
[filter:bulk]
use = egg:swift#bulk
[filter:ratelimit]
use = egg:swift#ratelimit
[filter:crossdomain]
use = egg:swift#crossdomain
[filter:dlo]
use = egg:swift#dlo
[filter:slo]
use = egg:swift#slo
[filter:container_sync]
use = egg:swift#container_sync
current = //saio/saio_endpoint
[filter:tempurl]
use = egg:swift#tempurl
[filter:tempauth]
use = egg:swift#tempauth
user_admin_admin = admin .admin .reseller_admin
user_test_tester = testing .admin
user_test2_tester2 = testing2 .admin
user_test_tester3 = testing3
[filter:staticweb]
use = egg:swift#staticweb
[filter:account-quotas]
use = egg:swift#account_quotas
[filter:container-quotas]
use = egg:swift#container_quotas
[filter:cache]
use = egg:swift#memcache
[filter:gatekeeper]
use = egg:swift#gatekeeper
[filter:versioned_writes]
use = egg:swift#versioned_writes
allow_versioned_writes = true
[filter:copy]
use = egg:swift#copy
[filter:listing_formats]
use = egg:swift#listing_formats
[filter:symlink]
use = egg:swift#symlink
# To enable, add the s3api middleware to the pipeline before tempauth
[filter:s3api]
use = egg:swift#s3api
[filter:keymaster]
use = egg:swift#keymaster
encryption_root_secret = changeme/changeme/changeme/changeme/change/=
# To enable use of encryption add both middlewares to pipeline, example:
# <other middleware> keymaster encryption proxy-logging proxy-server
[filter:encryption]
use = egg:swift#encryption
[app:proxy-server]
use = egg:swift#proxy
allow_account_management = true
account_autocreate = true
SWIFT_PROXY_SERVER_CONF
# ---------- [Object Expirer] ----------
cat << SWIFT_OBJECT_EXPIRER_CONF > /etc/swift/object-expirer.conf
[DEFAULT]
user = swift
log_name = object-expirer
log_facility = LOG_LOCAL6
log_level = INFO
[object-expirer]
interval = 300
[pipeline:main]
pipeline = catch_errors cache proxy-server
[app:proxy-server]
use = egg:swift#proxy
[filter:cache]
use = egg:swift#memcache
[filter:catch_errors]
use = egg:swift#catch_errors
SWIFT_OBJECT_EXPIRER_CONF
# ---------- [Container Recon] ----------
cat << SWIFT_CONTAINER_RECONCILER_CONF > /etc/swift/container-reconciler.conf
[DEFAULT]
user = swift
[container-reconciler]
[pipeline:main]
pipeline = catch_errors proxy-logging cache proxy-server
[app:proxy-server]
use = egg:swift#proxy
[filter:cache]
use = egg:swift#memcache
[filter:proxy-logging]
use = egg:swift#proxy_logging
[filter:catch_errors]
use = egg:swift#catch_errors
SWIFT_CONTAINER_RECONCILER_CONF
cat << SWIFT_CONTAINER_SYNC_REALMS_CONF > /etc/swift/container-sync-realms.conf
[saio]
key = changeme
key2 = changeme
cluster_saio_endpoint = http://127.0.0.1:8080/v1/
SWIFT_CONTAINER_SYNC_REALMS_CONF
# ---------- [Servers 1-4...] ----------
mkdir -p /etc/swift/account-server /etc/swift/container-server /etc/swift/object-server
for x in 1 2 3 4; do
# ---------- [Account Server] ----------
echo "writing /etc/swift/account-server/$x.conf..."
cat << SWIFT_ACCOUNT_SERVER_CONF > /etc/swift/account-server/$x.conf
[DEFAULT]
devices = /srv/$x/node
mount_check = false
disable_fallocate = true
bind_ip = 127.0.0.$x
bind_port = 60${x}2
workers = 1
user = swift
log_facility = LOG_LOCAL$((x + 1))
recon_cache_path = /var/cache/swift$x
eventlet_debug = true
[pipeline:main]
pipeline = healthcheck recon account-server
[app:account-server]
use = egg:swift#account
[filter:recon]
use = egg:swift#recon
[filter:healthcheck]
use = egg:swift#healthcheck
[account-replicator]
rsync_module = {replication_ip}::account{replication_port}
[account-auditor]
[account-reaper]
SWIFT_ACCOUNT_SERVER_CONF
# ---------- [Container Server] ----------
echo "writing /etc/swift/container-server/$x.conf..."
cat << SWIFT_CONTAINER_SERVER_CONF > /etc/swift/container-server/$x.conf
[DEFAULT]
devices = /srv/$x/node
mount_check = false
disable_fallocate = true
bind_ip = 127.0.0.$x
bind_port = 60${x}1
workers = 1
user = swift
log_facility = LOG_LOCAL$((x + 1))
recon_cache_path = /var/cache/swift$x
eventlet_debug = true
[pipeline:main]
pipeline = healthcheck recon container-server
[app:container-server]
use = egg:swift#container
[filter:recon]
use = egg:swift#recon
[filter:healthcheck]
use = egg:swift#healthcheck
[container-replicator]
rsync_module = {replication_ip}::container{replication_port}
[container-updater]
[container-auditor]
[container-sync]
[container-sharder]
auto_shard = true
rsync_module = {replication_ip}::container{replication_port}
# This is intentionally much smaller than the default of 1,000,000 so tests
# can run in a reasonable amount of time
shard_container_threshold = 100
# The probe tests make explicit assumptions about the batch sizes
shard_scanner_batch_size = 10
cleave_batch_size = 2
SWIFT_CONTAINER_SERVER_CONF
# ---------- [Object Server] ----------
echo "writing /etc/swift/object-server/$x.conf..."
cat << SWIFT_OBJECT_SERVER_CONF > /etc/swift/object-server/$x.conf
[DEFAULT]
devices = /srv/$x/node
mount_check = false
disable_fallocate = true
bind_ip = 127.0.0.$x
bind_port = 60${x}0
workers = 1
user = swift
log_facility = LOG_LOCAL$((x + 1))
recon_cache_path = /var/cache/swift$x
eventlet_debug = true
[pipeline:main]
pipeline = healthcheck recon object-server
[app:object-server]
use = egg:swift#object
[filter:recon]
use = egg:swift#recon
[filter:healthcheck]
use = egg:swift#healthcheck
[object-replicator]
rsync_module = {replication_ip}::object{replication_port}
[object-reconstructor]
[object-updater]
[object-auditor]
SWIFT_OBJECT_SERVER_CONF
done
chown -R swift:swift /etc/swift
# ---------- [Build Rings] ----------
rm -f /etc/swift/*.builder /etc/swift/*.ring.gz /etc/swift/backups/*.builder /etc/swift/backups/*.ring.gz
su swift -c "cd /etc/swift; swift-ring-builder object.builder create 10 3 1"
su swift -c "cd /etc/swift; swift-ring-builder object.builder add r1z1-127.0.0.1:6010/sdb1 1"
su swift -c "cd /etc/swift; swift-ring-builder object.builder add r1z2-127.0.0.2:6020/sdb2 1"
su swift -c "cd /etc/swift; swift-ring-builder object.builder add r1z3-127.0.0.3:6030/sdb3 1"
su swift -c "cd /etc/swift; swift-ring-builder object.builder add r1z4-127.0.0.4:6040/sdb4 1"
su swift -c "cd /etc/swift; swift-ring-builder object.builder rebalance"
su swift -c "cd /etc/swift; swift-ring-builder object-1.builder create 10 2 1"
su swift -c "cd /etc/swift; swift-ring-builder object-1.builder add r1z1-127.0.0.1:6010/sdb1 1"
su swift -c "cd /etc/swift; swift-ring-builder object-1.builder add r1z2-127.0.0.2:6020/sdb2 1"
su swift -c "cd /etc/swift; swift-ring-builder object-1.builder add r1z3-127.0.0.3:6030/sdb3 1"
su swift -c "cd /etc/swift; swift-ring-builder object-1.builder add r1z4-127.0.0.4:6040/sdb4 1"
su swift -c "cd /etc/swift; swift-ring-builder object-1.builder rebalance"
su swift -c "cd /etc/swift; swift-ring-builder object-2.builder create 10 6 1"
su swift -c "cd /etc/swift; swift-ring-builder object-2.builder add r1z1-127.0.0.1:6010/sdb1 1"
su swift -c "cd /etc/swift; swift-ring-builder object-2.builder add r1z1-127.0.0.1:6010/sdb5 1"
su swift -c "cd /etc/swift; swift-ring-builder object-2.builder add r1z2-127.0.0.2:6020/sdb2 1"
su swift -c "cd /etc/swift; swift-ring-builder object-2.builder add r1z2-127.0.0.2:6020/sdb6 1"
su swift -c "cd /etc/swift; swift-ring-builder object-2.builder add r1z3-127.0.0.3:6030/sdb3 1"
su swift -c "cd /etc/swift; swift-ring-builder object-2.builder add r1z3-127.0.0.3:6030/sdb7 1"
su swift -c "cd /etc/swift; swift-ring-builder object-2.builder add r1z4-127.0.0.4:6040/sdb4 1"
su swift -c "cd /etc/swift; swift-ring-builder object-2.builder add r1z4-127.0.0.4:6040/sdb8 1"
su swift -c "cd /etc/swift; swift-ring-builder object-2.builder rebalance"
su swift -c "cd /etc/swift; swift-ring-builder container.builder create 10 3 1"
su swift -c "cd /etc/swift; swift-ring-builder container.builder add r1z1-127.0.0.1:6011/sdb1 1"
su swift -c "cd /etc/swift; swift-ring-builder container.builder add r1z2-127.0.0.2:6021/sdb2 1"
su swift -c "cd /etc/swift; swift-ring-builder container.builder add r1z3-127.0.0.3:6031/sdb3 1"
su swift -c "cd /etc/swift; swift-ring-builder container.builder add r1z4-127.0.0.4:6041/sdb4 1"
su swift -c "cd /etc/swift; swift-ring-builder container.builder rebalance"
su swift -c "cd /etc/swift; swift-ring-builder account.builder create 10 3 1"
su swift -c "cd /etc/swift; swift-ring-builder account.builder add r1z1-127.0.0.1:6012/sdb1 1"
su swift -c "cd /etc/swift; swift-ring-builder account.builder add r1z2-127.0.0.2:6022/sdb2 1"
su swift -c "cd /etc/swift; swift-ring-builder account.builder add r1z3-127.0.0.3:6032/sdb3 1"
su swift -c "cd /etc/swift; swift-ring-builder account.builder add r1z4-127.0.0.4:6042/sdb4 1"
su swift -c "cd /etc/swift; swift-ring-builder account.builder rebalance"
# ========== [Run Swift] ==========
su swift -c "swift-init start main"
# ========== [Config/Run NFS] ==========
mkdir -p /var/nfs
touch /var/nfs/foo
touch /var/nfs/bar
adduser -D nfsnobody
chown nfsnobody:nfsnobody /var/nfs
chmod 755 /var/nfs
echo "/var/nfs 192.168.50.10(rw,sync,no_root_squash,no_subtree_check)" > /etc/exports
rc-update add nfs
rc-service nfs start
# ========== [Config/Run SMB] ==========
mkdir -p /var/smb
chmod 0777 /var/smb
touch /var/smb/baz
touch /var/smb/qux
printf "vagrant\\nvagrant\\n" | smbpasswd -a -s vagrant
cp /etc/samba/smb.conf /etc/samba/smb.conf.bak
cat << EOF > /etc/samba/smb.conf
[global]
workgroup = WORKGROUP
server string = Samba Server %v
dos charset = cp850
unix charset = ISO-8859-1
force user = vagrant
log file = /var/log/samba/log.%m
max log size = 50
security = user
[share]
path = /var/smb
valid users = vagrant
browseable = yes
writeable = yes
EOF
rc-update add samba
rc-service samba start
# ========== [Config/Run PSQL] ==========
rc-update add postgresql
rc-service postgresql start
echo "listen_addresses = '*'" >> /var/lib/postgresql/9.5/data/postgresql.conf
echo "host all all 192.168.50.10/0 md5" >> /var/lib/postgresql/9.5/data/pg_hba.conf
psql -U postgres -c "CREATE ROLE root WITH LOGIN CREATEDB SUPERUSER PASSWORD 'smartvm'" postgres
rc-service postgresql restart
# ========== [Config/Run FTP] ==========
cat << EOF > /etc/vsftpd/vsftpd.conf
listen=NO
listen_ipv6=YES
local_enable=YES
local_umask=022
write_enable=YES
connect_from_port_20=YES
anonymous_enable=YES
anon_root=/var/nfs/ftp/pub
anon_umask=022
anon_upload_enable=YES
anon_mkdir_write_enable=YES
anon_other_write_enable=YES
pam_service_name=vsftpd
userlist_enable=YES
userlist_deny=NO
seccomp_sandbox=NO
EOF
cat << EOF > /etc/vsftpd.user_list
vagrant
anonymous
EOF
mkdir -p /var/nfs/ftp/pub/uploads
chown -R ftp:ftp /var/nfs/ftp
chmod -R 555 /var/nfs/ftp/pub
chmod 777 /var/nfs/ftp/pub/uploads
rc-update add vsftpd
rc-service vsftpd start
BOOTSTRAP
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment