pennyworth-tool 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.hound.yml +3 -0
- data/.rspec +2 -0
- data/.rubocop.yml +18 -0
- data/CONTRIBUTING.md +67 -0
- data/COPYING +674 -0
- data/Gemfile +28 -0
- data/README.md +339 -0
- data/Rakefile +33 -0
- data/bin/pennyworth +26 -0
- data/config/setup.yml +17 -0
- data/examples/README.md +23 -0
- data/examples/kiwi/definitions/base_opensuse13.1_kvm/config.sh +87 -0
- data/examples/kiwi/definitions/base_opensuse13.1_kvm/config.xml +64 -0
- data/examples/kiwi/definitions/base_opensuse13.1_kvm/root/etc/sysconfig/network/ifcfg-eth0 +2 -0
- data/examples/kiwi/definitions/base_opensuse13.1_kvm/root/home/vagrant/.ssh/authorized_keys +1 -0
- data/examples/vagrant/Vagrantfile +14 -0
- data/files/99-libvirt.rules +2 -0
- data/files/image_test-template.xml +43 -0
- data/files/pool-default.xml +6 -0
- data/lib/image_runner.rb +89 -0
- data/lib/pennyworth.rb +65 -0
- data/lib/pennyworth/cli.rb +339 -0
- data/lib/pennyworth/cli_host_controller.rb +107 -0
- data/lib/pennyworth/commands/base_command.rb +96 -0
- data/lib/pennyworth/commands/boot_command.rb +29 -0
- data/lib/pennyworth/commands/build_base_command.rb +103 -0
- data/lib/pennyworth/commands/command.rb +43 -0
- data/lib/pennyworth/commands/down_command.rb +25 -0
- data/lib/pennyworth/commands/import_base_command.rb +112 -0
- data/lib/pennyworth/commands/import_ssh_keys_command.rb +27 -0
- data/lib/pennyworth/commands/list_command.rb +41 -0
- data/lib/pennyworth/commands/setup_command.rb +209 -0
- data/lib/pennyworth/commands/shutdown_command.rb +28 -0
- data/lib/pennyworth/commands/status_command.rb +26 -0
- data/lib/pennyworth/commands/up_command.rb +27 -0
- data/lib/pennyworth/exceptions.rb +39 -0
- data/lib/pennyworth/helper.rb +39 -0
- data/lib/pennyworth/host_config.rb +86 -0
- data/lib/pennyworth/host_runner.rb +133 -0
- data/lib/pennyworth/image_runner.rb +89 -0
- data/lib/pennyworth/libvirt.rb +93 -0
- data/lib/pennyworth/local_command_runner.rb +77 -0
- data/lib/pennyworth/local_runner.rb +34 -0
- data/lib/pennyworth/lock_service.rb +87 -0
- data/lib/pennyworth/remote_command_runner.rb +144 -0
- data/lib/pennyworth/runner.rb +27 -0
- data/lib/pennyworth/settings.rb +42 -0
- data/lib/pennyworth/spec.rb +96 -0
- data/lib/pennyworth/spec_profiler.rb +85 -0
- data/lib/pennyworth/ssh_keys_importer.rb +107 -0
- data/lib/pennyworth/urls.rb +28 -0
- data/lib/pennyworth/vagrant.rb +81 -0
- data/lib/pennyworth/vagrant_command.rb +120 -0
- data/lib/pennyworth/vagrant_runner.rb +44 -0
- data/lib/pennyworth/version.rb +22 -0
- data/lib/pennyworth/vm.rb +62 -0
- data/man/.gitignore +2 -0
- data/man/pennyworth.1.md +28 -0
- data/pennyworth.gemspec +57 -0
- data/prophet/Gemfile +3 -0
- data/prophet/prophet.rb +82 -0
- data/spec/base_command_spec.rb +30 -0
- data/spec/build_base_command_spec.rb +147 -0
- data/spec/cli_host_controller_spec.rb +113 -0
- data/spec/data/hosts.yaml +10 -0
- data/spec/data/kiwi/base_opensuse12.3_kvm.box +1 -0
- data/spec/data/kiwi/base_opensuse13.1_kvm.box +1 -0
- data/spec/data/kiwi/definitions/base_opensuse12.3_kvm/config.sh +1 -0
- data/spec/data/kiwi/definitions/base_opensuse12.3_kvm/config.xml +1 -0
- data/spec/data/kiwi/definitions/base_opensuse12.3_kvm/root/home/vagrant/.ssh/authorized_keys +1 -0
- data/spec/data/kiwi/definitions/base_opensuse13.1_kvm/config.sh +1 -0
- data/spec/data/kiwi/definitions/base_opensuse13.1_kvm/config.xml +1 -0
- data/spec/data/kiwi/definitions/base_opensuse13.1_kvm/root/home/vagrant/.ssh/authorized_keys +1 -0
- data/spec/data/kiwi2/box_state.yaml +14 -0
- data/spec/data/kiwi2/definitions/base_opensuse12.3_kvm/config.sh +1 -0
- data/spec/data/kiwi2/definitions/base_opensuse12.3_kvm/config.xml +1 -0
- data/spec/data/kiwi2/definitions/base_opensuse12.3_kvm/root/home/vagrant/.ssh/authorized_keys +1 -0
- data/spec/data/kiwi2/definitions/base_opensuse13.1_kvm/config.sh +1 -0
- data/spec/data/kiwi2/definitions/base_opensuse13.1_kvm/config.xml +1 -0
- data/spec/data/kiwi2/definitions/base_opensuse13.1_kvm/root/home/vagrant/.ssh/authorized_keys +1 -0
- data/spec/data/kiwi3/box_state.yaml +13 -0
- data/spec/data/kiwi3/definitions/base_opensuse12.3_kvm/.gitkeep +0 -0
- data/spec/data/kiwi3/definitions/base_opensuse13.1_kvm/.gitkeep +0 -0
- data/spec/data/kiwi3/import_state.yaml +3 -0
- data/spec/data/kiwi4/definitions/base_opensuse12.3_kvm/.gitkeep +0 -0
- data/spec/data/kiwi4/definitions/base_opensuse13.1_kvm/.gitkeep +0 -0
- data/spec/data/kiwi4/import_state.yaml +3 -0
- data/spec/data/kiwi5/import_state.yaml +3 -0
- data/spec/data/vagrant/.gitkeep +0 -0
- data/spec/host_config_spec.rb +197 -0
- data/spec/host_runner_spec.rb +112 -0
- data/spec/image_runner_spec.rb +62 -0
- data/spec/import_base_command_spec.rb +189 -0
- data/spec/local_command_runner_spec.rb +117 -0
- data/spec/local_runner_spec.rb +42 -0
- data/spec/lock_service_spec.rb +95 -0
- data/spec/remote_command_runner_spec.rb +115 -0
- data/spec/settings_spec.rb +26 -0
- data/spec/setup_command_spec.rb +49 -0
- data/spec/spec_helper.rb +50 -0
- data/spec/spec_profiler_spec.rb +63 -0
- data/spec/spec_spec.rb +99 -0
- data/spec/support/command_runner_examples.rb +29 -0
- data/spec/support/runner_examples.rb +34 -0
- data/spec/urls_spec.rb +46 -0
- data/spec/vagrant_command_spec.rb +51 -0
- data/spec/vagrant_runner_spec.rb +40 -0
- data/spec/vagrant_spec.rb +288 -0
- data/spec/vm_spec.rb +56 -0
- metadata +257 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Copyright (c) 2013-2015 SUSE LLC
|
|
2
|
+
#
|
|
3
|
+
# This program is free software; you can redistribute it and/or
|
|
4
|
+
# modify it under the terms of version 3 of the GNU General Public License as
|
|
5
|
+
# published by the Free Software Foundation.
|
|
6
|
+
#
|
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10
|
+
# GNU General Public License for more details.
|
|
11
|
+
#
|
|
12
|
+
# You should have received a copy of the GNU General Public License
|
|
13
|
+
# along with this program; if not, contact SUSE LLC.
|
|
14
|
+
#
|
|
15
|
+
# To contact SUSE about this file by physical or electronic mail,
|
|
16
|
+
# you may find current contact information at www.suse.com
|
|
17
|
+
|
|
18
|
+
module Pennyworth
|
|
19
|
+
class HostConfig
|
|
20
|
+
attr_reader :config_file, :lock_server_address
|
|
21
|
+
|
|
22
|
+
def self.for_directory(config_dir)
|
|
23
|
+
HostConfig.new(File.join(config_dir, "hosts.yaml"))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def initialize(config_file)
|
|
27
|
+
@config_file = File.expand_path(config_file)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def parse(yaml_string)
|
|
31
|
+
yaml = YAML.load(yaml_string)
|
|
32
|
+
if !yaml
|
|
33
|
+
raise HostFileError.new("Could not parse YAML in file '#{config_file}'")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
if yaml["include"]
|
|
37
|
+
begin
|
|
38
|
+
open(yaml["include"], "rb") do |u|
|
|
39
|
+
parse(u.read)
|
|
40
|
+
end
|
|
41
|
+
rescue OpenURI::HTTPError, Errno::ENOENT
|
|
42
|
+
raise HostFileError.new("Unable to include '#{yaml["include"]}'")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if yaml["hosts"]
|
|
47
|
+
if !@hosts
|
|
48
|
+
@hosts = yaml["hosts"]
|
|
49
|
+
else
|
|
50
|
+
yaml["hosts"].each do |key, value|
|
|
51
|
+
@hosts[key] = value
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
if yaml["lock_server_address"]
|
|
57
|
+
@lock_server_address = yaml["lock_server_address"]
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def read
|
|
62
|
+
parse(File.read(config_file))
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def hosts
|
|
66
|
+
@hosts.keys
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def host(host_name)
|
|
70
|
+
@hosts[host_name]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def setup(url)
|
|
74
|
+
if File.exist?(config_file)
|
|
75
|
+
raise HostFileError.new("Config file '#{config_file}' already exists." +
|
|
76
|
+
" Canceling setup.")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
FileUtils.mkdir_p(File.dirname(config_file))
|
|
80
|
+
File.open(config_file, "w") do |f|
|
|
81
|
+
f.puts("---")
|
|
82
|
+
f.puts("include: #{url}")
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Copyright (c) 2013-2014 SUSE LLC
|
|
2
|
+
#
|
|
3
|
+
# This program is free software; you can redistribute it and/or
|
|
4
|
+
# modify it under the terms of version 3 of the GNU General Public License as
|
|
5
|
+
# published by the Free Software Foundation.
|
|
6
|
+
#
|
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10
|
+
# GNU General Public License for more details.
|
|
11
|
+
#
|
|
12
|
+
# You should have received a copy of the GNU General Public License
|
|
13
|
+
# along with this program; if not, contact SUSE LLC.
|
|
14
|
+
#
|
|
15
|
+
# To contact SUSE about this file by physical or electronic mail,
|
|
16
|
+
# you may find current contact information at www.suse.com
|
|
17
|
+
|
|
18
|
+
module Pennyworth
|
|
19
|
+
class HostRunner < Runner
|
|
20
|
+
def initialize(host_name, host_config, username = "root")
|
|
21
|
+
@host_name = host_name
|
|
22
|
+
@username = username
|
|
23
|
+
config_file = host_config.config_file
|
|
24
|
+
|
|
25
|
+
host = host_config.host(host_name)
|
|
26
|
+
if !host
|
|
27
|
+
raise InvalidHostError.new("Host '#{host_name}' is not defined in '#{config_file}'")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
@ip = host["address"]
|
|
31
|
+
@base_snapshot_id = host["base_snapshot_id"]
|
|
32
|
+
if !@ip
|
|
33
|
+
raise InvalidHostError.new(
|
|
34
|
+
"Missing 'address' field for host '#{host_name}' in '#{config_file}'"
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
if should_cleanup && !@base_snapshot_id
|
|
38
|
+
raise InvalidHostError.new(
|
|
39
|
+
"Missing 'base_snapshot_id' field for host '#{host_name}' in '#{config_file}'"
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
@command_runner = RemoteCommandRunner.new(@ip, @username)
|
|
44
|
+
@locker = LockService.new(host_config.lock_server_address)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def start
|
|
48
|
+
if !@locker.request_lock(@host_name)
|
|
49
|
+
raise LockError.new("Host '#{@host_name}' already locked")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
connect
|
|
53
|
+
|
|
54
|
+
if should_cleanup
|
|
55
|
+
check_cleanup_capabilities
|
|
56
|
+
install_cleanup_interrupt_handler
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
@ip
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def cleanup
|
|
63
|
+
return if @cleaned_up || !@connected
|
|
64
|
+
|
|
65
|
+
remote = RemoteCommandRunner.new(@ip, @username)
|
|
66
|
+
remote.run "snapper", "create", "-c", "number", "--pre-number", @base_snapshot_id.to_s,
|
|
67
|
+
"--description", "pennyworth_snapshot"
|
|
68
|
+
remote.run "snapper", "cleanup", "number"
|
|
69
|
+
remote.run "snapper", "undochange", "#{@base_snapshot_id}..0"
|
|
70
|
+
remote.run "bash", "-c", "reboot &"
|
|
71
|
+
@cleaned_up = true
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def stop
|
|
75
|
+
if should_cleanup
|
|
76
|
+
cleanup
|
|
77
|
+
uninstall_cleanup_interrupt_handler
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
@locker.release_lock(@host_name)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
# Makes sure the we can connect to the remote system as root (without a
|
|
86
|
+
# password or passphrase)
|
|
87
|
+
def connect
|
|
88
|
+
Cheetah.run "ssh", "-q", "-o", "BatchMode=yes", "root@#{@ip}"
|
|
89
|
+
rescue Cheetah::ExecutionFailed
|
|
90
|
+
raise SshConnectionFailed.new(
|
|
91
|
+
"Could not establish SSH connection to host '#{@ip}'. Please make sure that " \
|
|
92
|
+
"you can connect non-interactively as root, e.g. using ssh-agent.\n\n" \
|
|
93
|
+
"To copy your default ssh key to the machine run:\n" \
|
|
94
|
+
"ssh-copy-id root@#{@ip}"
|
|
95
|
+
)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def check_cleanup_capabilities
|
|
99
|
+
begin
|
|
100
|
+
RemoteCommandRunner.new(@ip, @username).run "snapper", "--help"
|
|
101
|
+
rescue Cheetah::ExecutionFailed
|
|
102
|
+
raise CommandNotFoundError.new(
|
|
103
|
+
"Snapper needs to be installed on the test system '#{@ip}' for the automatic cleanup."
|
|
104
|
+
)
|
|
105
|
+
end
|
|
106
|
+
@connected = true
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def should_cleanup
|
|
110
|
+
!ENV["SKIP_HOST_CLEANUP"]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def install_cleanup_interrupt_handler
|
|
114
|
+
@old_interrupt_handler = trap("INT") do
|
|
115
|
+
trap("INT") do
|
|
116
|
+
exit!(1)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
puts "RSpec is shutting down. Resetting test host '#{@ip}'." \
|
|
120
|
+
"Interrupt again to force exit."
|
|
121
|
+
cleanup
|
|
122
|
+
puts "Done."
|
|
123
|
+
|
|
124
|
+
@old_interrupt_handler.call
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def uninstall_cleanup_interrupt_handler
|
|
129
|
+
# Restore old interrupt handler
|
|
130
|
+
trap("INT", @old_interrupt_handler) if defined?(@old_interrupt_handler)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Copyright (c) 2013-2014 SUSE LLC
|
|
2
|
+
#
|
|
3
|
+
# This program is free software; you can redistribute it and/or
|
|
4
|
+
# modify it under the terms of version 3 of the GNU General Public License as
|
|
5
|
+
# published by the Free Software Foundation.
|
|
6
|
+
#
|
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10
|
+
# GNU General Public License for more details.
|
|
11
|
+
#
|
|
12
|
+
# You should have received a copy of the GNU General Public License
|
|
13
|
+
# along with this program; if not, contact SUSE LLC.
|
|
14
|
+
#
|
|
15
|
+
# To contact SUSE about this file by physical or electronic mail,
|
|
16
|
+
# you may find current contact information at www.suse.com
|
|
17
|
+
|
|
18
|
+
module Pennyworth
|
|
19
|
+
class ImageRunner < Runner
|
|
20
|
+
DOMAIN_TEMPLATE = File.join(File.dirname(__FILE__) + "/../../files/image_test-template.xml")
|
|
21
|
+
|
|
22
|
+
attr_accessor :name
|
|
23
|
+
|
|
24
|
+
def initialize(image, username)
|
|
25
|
+
@image = image
|
|
26
|
+
@name = File.basename(image)
|
|
27
|
+
@username = username
|
|
28
|
+
|
|
29
|
+
@connection = ::Libvirt::open("qemu:///system")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def start
|
|
33
|
+
cleanup
|
|
34
|
+
|
|
35
|
+
ip = start_built_image
|
|
36
|
+
@command_runner = RemoteCommandRunner.new(ip, @username)
|
|
37
|
+
|
|
38
|
+
ip
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def stop
|
|
42
|
+
system = @connection.lookup_domain_by_name(@name)
|
|
43
|
+
system.destroy
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def cleanup_directory(_dir)
|
|
47
|
+
# The machine will be reset anyway after the tests, so this is is a NOP
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def cleanup
|
|
53
|
+
system = @connection.lookup_domain_by_name(@name)
|
|
54
|
+
system.destroy
|
|
55
|
+
rescue
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Creates a transient kvm domain from the predefined image_test-domain.xml
|
|
59
|
+
# file and returns the ip address for further interaction.
|
|
60
|
+
def start_built_image
|
|
61
|
+
domain_config = File.read(DOMAIN_TEMPLATE)
|
|
62
|
+
domain_config.gsub!("@@image@@", @image)
|
|
63
|
+
domain_config.gsub!("@@name@@", @name)
|
|
64
|
+
|
|
65
|
+
@connection.create_domain_xml(domain_config)
|
|
66
|
+
system = @connection.lookup_domain_by_name(@name)
|
|
67
|
+
|
|
68
|
+
domain_xml = Nokogiri::XML(system.xml_desc)
|
|
69
|
+
mac = domain_xml.xpath("//domain/devices/interface/mac").attr("address")
|
|
70
|
+
ip_address = nil
|
|
71
|
+
|
|
72
|
+
# Loop until the VM has got an IP address we can return
|
|
73
|
+
lease_file = "/var/lib/libvirt/dnsmasq/default.leases"
|
|
74
|
+
300.times do
|
|
75
|
+
match = File.readlines(lease_file).grep(/#{mac}/).first
|
|
76
|
+
if match
|
|
77
|
+
ip_address = match.split[2]
|
|
78
|
+
break
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
sleep 1
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
ip_address
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Copyright (c) 2013-2014 SUSE LLC
|
|
2
|
+
#
|
|
3
|
+
# This program is free software; you can redistribute it and/or
|
|
4
|
+
# modify it under the terms of version 3 of the GNU General Public License as
|
|
5
|
+
# published by the Free Software Foundation.
|
|
6
|
+
#
|
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10
|
+
# GNU General Public License for more details.
|
|
11
|
+
#
|
|
12
|
+
# You should have received a copy of the GNU General Public License
|
|
13
|
+
# along with this program; if not, contact SUSE LLC.
|
|
14
|
+
#
|
|
15
|
+
# To contact SUSE about this file by physical or electronic mail,
|
|
16
|
+
# you may find current contact information at www.suse.com
|
|
17
|
+
|
|
18
|
+
module Pennyworth
|
|
19
|
+
class Libvirt
|
|
20
|
+
LIBVIRT_NET_NAME = "default"
|
|
21
|
+
LIBVIRT_POOL_NAME = "default"
|
|
22
|
+
|
|
23
|
+
class << self
|
|
24
|
+
def ensure_libvirt_env_started
|
|
25
|
+
# The check here is unnecessary for technical reasons ("sysctl start" does
|
|
26
|
+
# not fail if the service is already running), but it avoids an unnecessary
|
|
27
|
+
# sudo password prompt.
|
|
28
|
+
libvirtd_start unless libvirtd_active?
|
|
29
|
+
|
|
30
|
+
libvirt_net_start unless libvirt_net_active?
|
|
31
|
+
|
|
32
|
+
# As default pool is not always available but gets sometimes created by
|
|
33
|
+
# tools like virt-manager, we need to create it ourself if it does not yet
|
|
34
|
+
# exist.
|
|
35
|
+
libvirt_pool_create unless libvirt_pool_exists?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def libvirtd_start
|
|
41
|
+
Cheetah.run "sudo", "systemctl", "start", "libvirtd"
|
|
42
|
+
|
|
43
|
+
sleep 0.1 until File.exists?("/var/run/libvirt/libvirt-sock")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def libvirtd_active?
|
|
47
|
+
output = Cheetah.run "systemctl", "show", "--property", "ActiveState", "libvirtd", :stdout => :capture
|
|
48
|
+
|
|
49
|
+
output == "ActiveState=active"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def libvirt_net_start
|
|
53
|
+
Cheetah.run "virsh", "-c", "qemu:///system", "net-start", LIBVIRT_NET_NAME
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def libvirt_net_active?
|
|
57
|
+
output = with_c_locale do
|
|
58
|
+
Cheetah.run "virsh", "-c", "qemu:///system", "net-list", "--all", :stdout => :capture
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# The output looks like this:
|
|
62
|
+
#
|
|
63
|
+
# Name State Autostart Persistent
|
|
64
|
+
# ----------------------------------------------------------
|
|
65
|
+
# default active no yes
|
|
66
|
+
# vagrant0 active yes yes
|
|
67
|
+
#
|
|
68
|
+
output.split("\n")[2..-1].find do |line|
|
|
69
|
+
line =~ /^\s*#{LIBVIRT_NET_NAME}\s+active/
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def libvirt_pool_create
|
|
74
|
+
pool_config_file = File.dirname(__FILE__) + "/../../files/pool-default.xml"
|
|
75
|
+
Cheetah.run "virsh", "-c", "qemu:///system", "pool-create", pool_config_file
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def libvirt_pool_exists?
|
|
79
|
+
output = with_c_locale do
|
|
80
|
+
Cheetah.run "virsh", "-c", "qemu:///system", "pool-list", "--all", :stdout => :capture
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# The output looks like this:
|
|
84
|
+
#
|
|
85
|
+
# Name State Autostart
|
|
86
|
+
# -----------------------------------------
|
|
87
|
+
# default active no
|
|
88
|
+
#
|
|
89
|
+
output.split("\n")[2..-1].detect { |line| line =~ /^\s*#{LIBVIRT_POOL_NAME}/ }
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Copyright (c) 2013-2014 SUSE LLC
|
|
2
|
+
#
|
|
3
|
+
# This program is free software; you can redistribute it and/or
|
|
4
|
+
# modify it under the terms of version 3 of the GNU General Public License as
|
|
5
|
+
# published by the Free Software Foundation.
|
|
6
|
+
#
|
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10
|
+
# GNU General Public License for more details.
|
|
11
|
+
#
|
|
12
|
+
# You should have received a copy of the GNU General Public License
|
|
13
|
+
# along with this program; if not, contact SUSE LLC.
|
|
14
|
+
#
|
|
15
|
+
# To contact SUSE about this file by physical or electronic mail,
|
|
16
|
+
# you may find current contact information at www.suse.com
|
|
17
|
+
|
|
18
|
+
module Pennyworth
|
|
19
|
+
# LocalCommandRunner is used for executing commands on the local machine
|
|
20
|
+
class LocalCommandRunner
|
|
21
|
+
# Initialize the command runner
|
|
22
|
+
#
|
|
23
|
+
# +opts+:: Options to modify how and which the commands are run.
|
|
24
|
+
#
|
|
25
|
+
# Available options:
|
|
26
|
+
# [env]:: Hash of environment options to set for the command, e.g.
|
|
27
|
+
# {
|
|
28
|
+
# "MACHINERY_DIR" => "/tmp/machinery"
|
|
29
|
+
# }
|
|
30
|
+
def initialize(opts = {})
|
|
31
|
+
@env = opts[:env] || {}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def run(*args)
|
|
35
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
36
|
+
|
|
37
|
+
with_env(@env) do
|
|
38
|
+
Cheetah.run(
|
|
39
|
+
"bash", "-c", *args, options
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
rescue Cheetah::ExecutionFailed => e
|
|
43
|
+
raise ExecutionFailed.new(e)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Copy a local file to the remote system.
|
|
47
|
+
#
|
|
48
|
+
# +source+:: Path to the local file
|
|
49
|
+
# +destination+:: Path to the remote file or directory. If +destination+ is a
|
|
50
|
+
# path, the same filename as +source+ will be used.
|
|
51
|
+
# +opts+:: Options to modify the attributes of the remote file.
|
|
52
|
+
#
|
|
53
|
+
# Available options:
|
|
54
|
+
# [owner]:: Owner of the file, e.g. "tux"
|
|
55
|
+
# [group]:: Group of the file, e.g. "users"
|
|
56
|
+
# [mode]:: Mode of the file, e.g. "600"
|
|
57
|
+
def inject_file(source, destination, opts = {})
|
|
58
|
+
# Append filename (taken from +source+) to destination if it is a path, so
|
|
59
|
+
# that +destination+ is always the full target path including the filename.
|
|
60
|
+
destination = File.join(destination, File.basename(source)) if File.directory?(destination)
|
|
61
|
+
|
|
62
|
+
FileUtils.cp(source, destination)
|
|
63
|
+
FileUtils.chown(opts[:owner], opts[:group], destination)
|
|
64
|
+
FileUtils.chmod(opts[:mode], destination) if opts[:mode]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def extract_file(source, destination)
|
|
68
|
+
FileUtils.cp(source, destination)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def inject_directory(source, destination, opts = {})
|
|
72
|
+
FileUtils.mkdir_p(destination)
|
|
73
|
+
FileUtils.cp_r(source, destination)
|
|
74
|
+
FileUtils.chown_R(opts[:owner], opts[:group], destination)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|