rudy 0.6.8 → 0.7.0
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.
- data/CHANGES.txt +15 -2
- data/README.rdoc +30 -23
- data/Rakefile +5 -5
- data/Rudyfile +87 -66
- data/bin/rudy +120 -167
- data/bin/rudy-ec2 +17 -13
- data/bin/rudy-s3 +6 -4
- data/bin/rudy-sdb +5 -4
- data/lib/annoy.rb +1 -1
- data/lib/console.rb +1 -3
- data/lib/rudy.rb +11 -25
- data/lib/rudy/aws/ec2/instance.rb +1 -1
- data/lib/rudy/aws/ec2/volume.rb +2 -2
- data/lib/rudy/aws/sdb/error.rb +2 -1
- data/lib/rudy/cli.rb +10 -1
- data/lib/rudy/cli/aws/ec2/addresses.rb +1 -1
- data/lib/rudy/cli/aws/ec2/images.rb +3 -1
- data/lib/rudy/cli/aws/ec2/instances.rb +2 -2
- data/lib/rudy/cli/candy.rb +11 -0
- data/lib/rudy/cli/config.rb +25 -44
- data/lib/rudy/cli/machines.rb +30 -10
- data/lib/rudy/cli/routines.rb +67 -19
- data/lib/rudy/config.rb +30 -13
- data/lib/rudy/config/objects.rb +135 -10
- data/lib/rudy/disks.rb +8 -52
- data/lib/rudy/global.rb +9 -5
- data/lib/rudy/guidelines.rb +18 -0
- data/lib/rudy/huxtable.rb +29 -19
- data/lib/rudy/machines.rb +10 -7
- data/lib/rudy/mixins/hash.rb +25 -0
- data/lib/rudy/routines.rb +160 -10
- data/lib/rudy/routines/helper.rb +50 -0
- data/lib/rudy/routines/helpers/diskhelper.rb +44 -18
- data/lib/rudy/routines/helpers/scmhelper.rb +39 -0
- data/lib/rudy/routines/helpers/scripthelper.rb +86 -35
- data/lib/rudy/routines/helpers/userhelper.rb +37 -0
- data/lib/rudy/routines/passthrough.rb +36 -0
- data/lib/rudy/routines/release.rb +38 -22
- data/lib/rudy/routines/shutdown.rb +20 -49
- data/lib/rudy/routines/startup.rb +20 -47
- data/lib/rudy/scm.rb +75 -0
- data/lib/rudy/scm/git.rb +215 -0
- data/lib/rudy/scm/svn.rb +7 -6
- data/lib/rudy/utils.rb +12 -30
- data/lib/storable.rb +4 -1
- data/lib/sysinfo.rb +10 -0
- data/rudy.gemspec +21 -9
- data/test/01_mixins/10_hash_test.rb +25 -0
- data/test/{05_config → 10_config}/00_setup_test.rb +1 -1
- data/test/{05_config → 10_config}/30_machines_test.rb +1 -1
- data/test/15_scm/00_setup_test.rb +20 -0
- data/test/15_scm/20_git_test.rb +61 -0
- data/test/helper.rb +1 -1
- data/vendor/highline-1.5.1/Rakefile +3 -3
- metadata +41 -12
- data/bin/ird +0 -175
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Rudy
|
4
|
+
module Routines
|
5
|
+
module HelperBase
|
6
|
+
include Rudy::Huxtable
|
7
|
+
|
8
|
+
def execute_rbox_command(ret=nil, &command)
|
9
|
+
begin
|
10
|
+
ret = command.call
|
11
|
+
puts ' ' << ret.stdout.join("#{$/} ") if !ret.stdout.empty?
|
12
|
+
print_response(ret)
|
13
|
+
rescue Rye::CommandError => ex
|
14
|
+
print_response(ex)
|
15
|
+
exit 12 unless keep_going?
|
16
|
+
rescue Rye::CommandNotFound => ex
|
17
|
+
STDERR.puts " CommandNotFound: #{ex.message}".color(:red)
|
18
|
+
STDERR.puts ex.backtrace if Rudy.debug?
|
19
|
+
exit 12 unless keep_going?
|
20
|
+
end
|
21
|
+
|
22
|
+
ret
|
23
|
+
end
|
24
|
+
|
25
|
+
def keep_going?
|
26
|
+
Annoy.pose_question(" Keep going?\a ", /yes|y|ya|sure|you bet!/i, STDERR)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns a formatted string for printing command info
|
30
|
+
def command_separator(cmd, user)
|
31
|
+
cmd ||= ""
|
32
|
+
cmd, user = cmd.to_s, user.to_s
|
33
|
+
prompt = user == "root" ? "#" : "$"
|
34
|
+
("%s%s%s %s" % [$/, user, prompt, cmd.bright])
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def print_response(rap)
|
39
|
+
colour = rap.exit_code != 0 ? :red : :normal
|
40
|
+
[:stderr].each do |sumpin|
|
41
|
+
next if rap.send(sumpin).empty?
|
42
|
+
STDERR.puts (" #{sumpin.upcase.to_s} " << '-'*38).color(colour).bright
|
43
|
+
STDERR.puts " " << rap.send(sumpin).join("#{$/} ").color(colour)
|
44
|
+
end
|
45
|
+
STDERR.puts " Exit code: #{rap.exit_code}".color(colour) if rap.exit_code != 0
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -2,8 +2,19 @@
|
|
2
2
|
|
3
3
|
module Rudy; module Routines;
|
4
4
|
module DiskHelper
|
5
|
+
include Rudy::Routines::HelperBase # TODO: use execute_rbox_command
|
5
6
|
extend self
|
6
7
|
|
8
|
+
def disks?(routine)
|
9
|
+
(routine.is_a?(Caesars::Hash) && routine.disks &&
|
10
|
+
routine.disks.is_a?(Caesars::Hash) && !routine.disks.empty?)
|
11
|
+
end
|
12
|
+
|
13
|
+
def paths(routine)
|
14
|
+
return nil unless disks?(routine)
|
15
|
+
routine.disks.values.collect { |d| d.keys }.flatten
|
16
|
+
end
|
17
|
+
|
7
18
|
def execute(routine, machine, rbox)
|
8
19
|
return unless routine
|
9
20
|
raise "Not a Rudy::Machine" unless machine.is_a?(Rudy::Machine)
|
@@ -12,13 +23,20 @@ module Rudy; module Routines;
|
|
12
23
|
@machine = machine
|
13
24
|
@rbox = rbox
|
14
25
|
|
15
|
-
|
26
|
+
# We need to add mkfs since it's not enabled by default.
|
27
|
+
# We add it only to this instance we're using.
|
28
|
+
def @rbox.mkfs(*args); cmd('mkfs', args); end
|
29
|
+
|
30
|
+
return unless disks?(routine)
|
31
|
+
|
32
|
+
routine.disks.each_pair do |action, disks|
|
16
33
|
unless DiskHelper.respond_to?(action)
|
17
34
|
STDERR.puts %Q(DiskHelper: unknown action "#{action}")
|
18
35
|
next
|
19
36
|
end
|
20
37
|
send(action, disks) # create, copy, destroy, ...
|
21
38
|
end
|
39
|
+
|
22
40
|
end
|
23
41
|
|
24
42
|
def create(disks)
|
@@ -26,18 +44,23 @@ module Rudy; module Routines;
|
|
26
44
|
|
27
45
|
disks.each_pair do |path, props|
|
28
46
|
disk = Rudy::Disk.new(path, props[:size], props[:device], @machine.position)
|
29
|
-
|
47
|
+
olddisk = rdisk.get(disk.name)
|
48
|
+
if olddisk && olddisk.exists?
|
49
|
+
puts "Disk exists: #{olddisk.name}".color(:red)
|
50
|
+
return
|
51
|
+
end
|
52
|
+
|
30
53
|
puts "Creating #{disk.name} "
|
31
54
|
|
32
|
-
|
55
|
+
msg = "Creating volume... "
|
33
56
|
disk.create
|
34
|
-
Rudy::Utils.waiter(2, 60, STDOUT,
|
57
|
+
Rudy::Utils.waiter(2, 60, STDOUT, msg) {
|
35
58
|
disk.available?
|
36
59
|
}
|
37
60
|
|
38
|
-
|
61
|
+
msg = "Attaching #{disk.awsid} to #{@machine.awsid}... "
|
39
62
|
disk.attach(@machine.awsid)
|
40
|
-
Rudy::Utils.waiter(2,
|
63
|
+
Rudy::Utils.waiter(2, 10, STDOUT, msg) {
|
41
64
|
disk.attached?
|
42
65
|
}
|
43
66
|
|
@@ -46,15 +69,19 @@ module Rudy; module Routines;
|
|
46
69
|
# "No such file or directory while trying to determine filesystem size"
|
47
70
|
sleep 2
|
48
71
|
|
72
|
+
# TODO: Cleanup. See ScriptHelper
|
49
73
|
begin
|
50
74
|
print "Creating ext3 filesystem for #{disk.device}... "
|
51
|
-
|
52
|
-
|
53
|
-
|
75
|
+
execute_rbox_command {
|
76
|
+
|
77
|
+
@rbox.mkfs(:t, "ext3", :F, disk.device)
|
78
|
+
@rbox.mkdir(:p, disk.path)
|
79
|
+
puts "done"
|
54
80
|
|
55
|
-
|
81
|
+
print "Mounting at #{disk.path}... "
|
56
82
|
|
57
|
-
|
83
|
+
@rbox.mount(:t, 'ext3', disk.device, disk.path)
|
84
|
+
}
|
58
85
|
disk.mounted = true
|
59
86
|
disk.save
|
60
87
|
rescue Net::SSH::AuthenticationFailed, Net::SSH::HostKeyMismatch => ex
|
@@ -85,24 +112,23 @@ module Rudy; module Routines;
|
|
85
112
|
puts "Destroying #{disk.name}"
|
86
113
|
|
87
114
|
if disk.mounted?
|
88
|
-
print "Unmounting #{disk.path}...
|
89
|
-
@rbox.umount(disk.path)
|
115
|
+
print "Unmounting #{disk.path}..."
|
116
|
+
execute_rbox_command { @rbox.umount(disk.path) }
|
117
|
+
puts " done"
|
90
118
|
sleep 0.5
|
91
|
-
puts "done"
|
92
119
|
end
|
93
120
|
|
94
121
|
if disk.attached?
|
95
|
-
|
122
|
+
msg = "Detaching #{disk.awsid}..."
|
96
123
|
disk.detach
|
97
|
-
Rudy::Utils.waiter(2, 60, STDOUT,
|
124
|
+
Rudy::Utils.waiter(2, 60, STDOUT, msg) {
|
98
125
|
disk.available?
|
99
126
|
}
|
100
127
|
sleep 0.5
|
101
128
|
end
|
102
129
|
|
103
|
-
|
130
|
+
puts "Destroying metadata... "
|
104
131
|
disk.destroy
|
105
|
-
puts "done"
|
106
132
|
|
107
133
|
end
|
108
134
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
|
2
|
+
module Rudy; module Routines;
|
3
|
+
module SCMHelper
|
4
|
+
include Rudy::Routines::HelperBase
|
5
|
+
extend self
|
6
|
+
|
7
|
+
# Does the routine config contain SCM routines?
|
8
|
+
# Raises Rudy::Error if there is malformed configuration.
|
9
|
+
def scm?(routine)
|
10
|
+
scmnames = SUPPORTED_SCM_NAMES & routine.keys # Find intersections.
|
11
|
+
return false if scmnames.empty? # Nothing to do.
|
12
|
+
scmnames.each do |scm|
|
13
|
+
routine[scm].values.each do |p| # Each SCM should have a
|
14
|
+
raise "Bad #{scm} config" if !p.kind_of?(Hash) # Hash config. Otherwise
|
15
|
+
end # it's misconfigured.
|
16
|
+
end
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_scm_objects(routine)
|
21
|
+
return nil unless routine
|
22
|
+
scmnames = SUPPORTED_SCM_NAMES & routine.keys
|
23
|
+
vlist = []
|
24
|
+
# Look for scm config in the routine by checking all known scm types.
|
25
|
+
# For each one we'll create an instance of the appropriate SCM class.
|
26
|
+
scmnames.each do |scm|
|
27
|
+
routine[scm].each_pair do |user,params|
|
28
|
+
klass = eval "Rudy::SCM::#{scm.to_s.upcase}"
|
29
|
+
params[:user] = user
|
30
|
+
scm = klass.new(params)
|
31
|
+
scm.raise_early_exceptions # Raises exceptions for obvious problems.
|
32
|
+
vlist << scm
|
33
|
+
end
|
34
|
+
end
|
35
|
+
vlist
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end; end
|
@@ -1,34 +1,69 @@
|
|
1
1
|
require 'tempfile'
|
2
2
|
|
3
|
-
|
4
3
|
module Rudy; module Routines;
|
5
|
-
|
4
|
+
#--
|
5
|
+
# TODO: Rename to ShellHelper
|
6
|
+
#++
|
6
7
|
module ScriptHelper
|
8
|
+
include Rudy::Routines::HelperBase # TODO: use execute_rbox_command
|
7
9
|
extend self
|
8
|
-
|
10
|
+
|
9
11
|
@@script_types = [:after, :before, :after_local, :before_local]
|
10
12
|
@@script_config_file = "rudy-config.yml"
|
11
13
|
|
12
14
|
def before_local(routine, sconf, rbox)
|
15
|
+
|
16
|
+
# before_local generally doesn't take a user name like the remote
|
17
|
+
# before block so we add it here (unless the user did specify it)
|
18
|
+
routine[:before_local] = {
|
19
|
+
rbox.user.to_sym => routine.delete(:before_local)
|
20
|
+
} unless routine.has_key?(rbox.user.to_sym) # use routine[timing].deepest_point ?
|
13
21
|
execute_command(:before_local, routine, sconf, 'localhost', rbox)
|
14
|
-
end
|
22
|
+
end
|
23
|
+
def before_local?(routine); execute_command?(:before_local, routine); end
|
24
|
+
|
15
25
|
def after_local(routine, sconf, rbox)
|
26
|
+
routine[:after_local] = { # See before_local note
|
27
|
+
rbox.user.to_sym => routine.delete(:after_local)
|
28
|
+
} unless routine.has_key?(rbox.user.to_sym)
|
16
29
|
execute_command(:after_local, routine, sconf, 'localhost', rbox)
|
17
30
|
end
|
18
|
-
|
31
|
+
def after_local?(routine); execute_command?(:after_local, routine); end
|
32
|
+
|
33
|
+
|
19
34
|
def before(routine, sconf, machine, rbox)
|
20
35
|
raise "ScriptHelper: Not a Rudy::Machine" unless machine.is_a?(Rudy::Machine)
|
21
36
|
execute_command(:before, routine, sconf, machine.name, rbox)
|
22
37
|
end
|
38
|
+
def before?(routine); execute_command?(:before, routine); end
|
39
|
+
|
23
40
|
def after(routine, sconf, machine, rbox)
|
24
41
|
raise "ScriptHelper: Not a Rudy::Machine" unless machine.is_a?(Rudy::Machine)
|
25
42
|
execute_command(:after, routine, sconf, machine.name, rbox)
|
26
43
|
end
|
44
|
+
def after?(routine); execute_command?(:after, routine); end
|
27
45
|
|
28
46
|
|
29
47
|
private
|
30
48
|
|
31
|
-
#
|
49
|
+
# Does the routine have the requested script type?
|
50
|
+
# * +timing+ is one of: after, before, after_local, before_local
|
51
|
+
# * +routine+ a single routine hash (startup, shutdown, etc...)
|
52
|
+
# Prints notice to STDERR if there's an empty conf hash
|
53
|
+
def execute_command?(timing, routine)
|
54
|
+
hasconf = (routine.is_a?(Caesars::Hash) && routine.has_key?(timing))
|
55
|
+
return false unless hasconf
|
56
|
+
routine[timing].each_pair do |user,conf|
|
57
|
+
if conf.empty?
|
58
|
+
STDERR.puts "Empty #{timing} config for #{user}"
|
59
|
+
else
|
60
|
+
return true
|
61
|
+
end
|
62
|
+
end
|
63
|
+
false
|
64
|
+
end
|
65
|
+
|
66
|
+
# * +timing+ is one of: after, before, after_local, before_local
|
32
67
|
# * +routine+ a single routine hash (startup, shutdown, etc...)
|
33
68
|
# * +sconf+ is a config hash from machines config (ignored if nil)
|
34
69
|
# * +hostname+ machine hostname that we're working on
|
@@ -47,48 +82,64 @@ module Rudy; module Routines;
|
|
47
82
|
# add the method on for the instance of rbox we are using.
|
48
83
|
def rbox.rm(*args); cmd('rm', args); end
|
49
84
|
|
50
|
-
|
51
|
-
if routine.is_a?(Caesars::Hash) && routine.has_key?(timing)
|
52
|
-
puts "Connecting to #{hostname}"
|
53
|
-
begin
|
54
|
-
rbox.connect
|
55
|
-
rescue Net::SSH::AuthenticationFailed, Net::SSH::HostKeyMismatch => ex
|
56
|
-
STDERR.puts "Error connecting: #{ex.message}".color(:red)
|
57
|
-
STDERR.puts "Skipping scripts".color(:red)
|
58
|
-
end
|
85
|
+
if execute_command?(timing, routine) # i.e. before_local?
|
59
86
|
|
60
87
|
original_user = rbox.user
|
61
|
-
|
62
|
-
|
88
|
+
users = routine[timing] || {}
|
89
|
+
users.each_pair do |user, commands|
|
63
90
|
|
64
91
|
begin
|
65
|
-
user, command, *args = script.to_a.flatten.compact
|
66
92
|
rbox.switch_user user # does nothing if it's the same user
|
67
|
-
|
68
|
-
|
69
|
-
puts
|
70
|
-
|
71
|
-
|
72
|
-
|
93
|
+
rbox.connect(false) # does nothing if already connected
|
94
|
+
rescue Net::SSH::AuthenticationFailed, Net::SSH::HostKeyMismatch => ex
|
95
|
+
STDERR.puts "Error connecting: #{ex.message}".color(:red)
|
96
|
+
STDERR.puts "Skipping user #{user}".color(:red)
|
97
|
+
next
|
98
|
+
end
|
73
99
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
100
|
+
execute_rbox_command {
|
101
|
+
# We need to create the config file for every script,
|
102
|
+
# b/c the user may change and it would not be accessible.
|
103
|
+
# We turn off safe mode so we can write the config file via SSH.
|
104
|
+
# This will need to use SCP eventually; it is unsafe and error prone.
|
105
|
+
# TODO: Replace with rbox.upload. Make it safe again!
|
106
|
+
conf_str = StringIO.new
|
107
|
+
conf_str.puts sconf.to_hash.to_yaml
|
108
|
+
rbox.upload(conf_str, @@script_config_file)
|
109
|
+
rbox.chmod(600, @@script_config_file)
|
110
|
+
}
|
111
|
+
|
112
|
+
commands.each_pair do |command, calls|
|
113
|
+
# If a command is only referred to once and it has no arguments
|
114
|
+
# defined, we force it through by making an array with one element.
|
115
|
+
calls = [[]] if calls.empty?
|
116
|
+
|
117
|
+
# Force a CommandNotFound exception early
|
118
|
+
unless rbox.can?(command)
|
119
|
+
execute_rbox_command { rbox.send(command) }
|
120
|
+
next
|
121
|
+
end
|
122
|
+
|
123
|
+
# Execute the command for every set of arguments
|
124
|
+
calls.each do |args|
|
125
|
+
puts command_separator(rbox.preview_command(command, args), user)
|
126
|
+
execute_rbox_command { ret = rbox.send(command, args) }
|
81
127
|
end
|
82
|
-
rescue Rye::CommandNotFound => ex
|
83
|
-
puts " CommandNotFound: #{ex.message}".color(:red)
|
84
128
|
end
|
85
129
|
|
130
|
+
# I was gettings errors about script_config_file not existing. There
|
131
|
+
# might be a race condition when the rm command is called too quickly.
|
132
|
+
# It's also quite possible I'm off my rocker!
|
133
|
+
sleep 0.1
|
86
134
|
|
87
|
-
rbox.
|
135
|
+
rbox.cd # reset to home dir
|
136
|
+
#rbox.rm(@@script_config_file)
|
88
137
|
end
|
138
|
+
|
139
|
+
# Return the borrowed rbox instance to the user it was provided with
|
89
140
|
rbox.switch_user original_user
|
90
141
|
else
|
91
|
-
|
142
|
+
puts "Nothing to do"
|
92
143
|
end
|
93
144
|
|
94
145
|
tf.delete # delete local copy of script config
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
module Rudy; module Routines;
|
3
|
+
module UserHelper
|
4
|
+
include Rudy::Routines::HelperBase # TODO: use execute_rbox_command
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def adduser?(routine)
|
8
|
+
(!routine.adduser.nil? && !routine.adduser.empty?)
|
9
|
+
end
|
10
|
+
def adduser(routine, machine, rbox)
|
11
|
+
|
12
|
+
# On Solaris, the user's home directory needs to be specified
|
13
|
+
# explicitly so we do it for linux too for fun.
|
14
|
+
homedir = rbox.guess_user_home(routine.adduser.to_s)
|
15
|
+
args = [:m, :d, homedir, :s, '/bin/bash', routine.adduser.to_s]
|
16
|
+
puts command_separator(rbox.preview_command(:useradd, args), routine.adduser.to_s)
|
17
|
+
|
18
|
+
# NOTE: We'll may to use platform specific code here.
|
19
|
+
# Linux has adduser and useradd commands:
|
20
|
+
# adduser can prompt for info which we don't want.
|
21
|
+
# useradd does not prompt (on Debian/Ubuntu at least).
|
22
|
+
# We need to specify bash b/c the default is /bin/sh
|
23
|
+
execute_rbox_command { rbox.useradd(args) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def authorize?(routine)
|
27
|
+
(!routine.authorize.nil? && !routine.authorize.empty?)
|
28
|
+
end
|
29
|
+
def authorize(routine, machine, rbox)
|
30
|
+
puts command_separator(:authorize_keys_remote, routine.authorize)
|
31
|
+
execute_rbox_command { rbox.authorize_keys_remote(routine.authorize) }
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end; end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Rudy; module Routines;
|
4
|
+
class Passthrough < Rudy::Routines::Base
|
5
|
+
|
6
|
+
def init(*args)
|
7
|
+
@routine_name = args.first
|
8
|
+
@routine = fetch_routine_config(@routine_name)
|
9
|
+
end
|
10
|
+
|
11
|
+
# * +each_mach+ is an optional block which is executed between
|
12
|
+
# disk creation and the after scripts. The will receives two
|
13
|
+
# arguments: instances of Rudy::Machine and Rye::Box.
|
14
|
+
def execute(&each_mach)
|
15
|
+
routine_separator(@routine_name)
|
16
|
+
machines = []
|
17
|
+
generic_machine_runner(:list) do |machine|
|
18
|
+
puts $/ #, "[routine: #{@routine_name}]"
|
19
|
+
machines << machine
|
20
|
+
end
|
21
|
+
machines
|
22
|
+
end
|
23
|
+
|
24
|
+
# Called by generic_machine_runner
|
25
|
+
def raise_early_exceptions
|
26
|
+
raise Rudy::Error, "No routine name" unless @routine_name
|
27
|
+
raise NoRoutine, @routine_name unless @routine
|
28
|
+
rmach = Rudy::Machines.new
|
29
|
+
raise Rudy::PrivateKeyNotFound, root_keypairpath unless has_keypair?(:root)
|
30
|
+
raise MachineGroupNotDefined, current_machine_group unless known_machine_group?
|
31
|
+
raise MachineGroupNotRunning, current_machine_group unless rmach.running?
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end; end
|