restfully-addons 1.0.0 → 1.1.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/examples/bonfire/scenario1.rb +205 -0
- data/examples/bonfire/scenario2.rb +198 -0
- data/examples/bonfire/ssh.rb +107 -0
- data/lib/restfully/addons/bonfire/ssh.rb +17 -4
- data/lib/restfully/addons/version.rb +1 -1
- metadata +19 -16
@@ -0,0 +1,205 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'restfully'
|
3
|
+
require 'restfully/addons/bonfire'
|
4
|
+
|
5
|
+
CLIENT_IMAGE_NAME = "VM-iperf"
|
6
|
+
SERVER_IMAGE_NAME = "VM-iperf"
|
7
|
+
AGGREGATOR_IMAGE_NAME = "BonFIRE Zabbix Aggregator v4"
|
8
|
+
WAN_NAME = "BonFIRE WAN"
|
9
|
+
|
10
|
+
session = Restfully::Session.new(
|
11
|
+
:configuration_file => "~/.restfully/api.bonfire-project.eu",
|
12
|
+
:cache => false,
|
13
|
+
:gateway => "ssh.bonfire.grid5000.fr",
|
14
|
+
:keys => ["~/.ssh/id_rsa"],
|
15
|
+
:require => ["http://bonfire-dev.gforge.inria.fr/public/restfully/http.rb"]
|
16
|
+
)
|
17
|
+
session.logger.level = Logger::INFO
|
18
|
+
|
19
|
+
experiment = nil
|
20
|
+
|
21
|
+
begin
|
22
|
+
# Find an existing running experiment with the same name or submit a new
|
23
|
+
# one. This allows re-using an experiment when developing a new script.
|
24
|
+
experiment = session.root.experiments.find{|e|
|
25
|
+
e['name'] == "Demo ONE" && e['status'] == "running"
|
26
|
+
} || session.root.experiments.submit(
|
27
|
+
:name => "Demo ONE",
|
28
|
+
:description => "ONE demo using Restfully - #{Time.now.to_s}",
|
29
|
+
:status => "waiting",
|
30
|
+
:walltime => 8*3600 # 8 hours
|
31
|
+
)
|
32
|
+
|
33
|
+
# Create shortcuts for location resources:
|
34
|
+
inria = session.root.locations[:'fr-inria']
|
35
|
+
fail "Can't select the fr-inria location" if inria.nil?
|
36
|
+
hlrs = session.root.locations[:'de-hlrs']
|
37
|
+
fail "Can't select the de-hlrs location" if hlrs.nil?
|
38
|
+
epcc = session.root.locations[:'uk-epcc']
|
39
|
+
fail "Can't select the uk-epcc location" if epcc.nil?
|
40
|
+
|
41
|
+
# In this array we'll store the clients launched at each site:
|
42
|
+
locations = [[epcc,[]], [hlrs,[]], [inria,[]]]
|
43
|
+
|
44
|
+
session.logger.info "Launching aggregator..."
|
45
|
+
# Find an existing server in the experiment, or set up a new one:
|
46
|
+
aggregator = experiment.computes.find{|vm|
|
47
|
+
vm['name'] == "BonFIRE-monitor-experiment#{experiment['id']}"
|
48
|
+
} || experiment.computes.submit(
|
49
|
+
:name => "BonFIRE-monitor-experiment#{experiment['id']}",
|
50
|
+
:instance_type => "small",
|
51
|
+
:disk => [{
|
52
|
+
:storage => inria.storages.find{|s|
|
53
|
+
s['name'] == AGGREGATOR_IMAGE_NAME
|
54
|
+
}, :type => "OS"
|
55
|
+
}],
|
56
|
+
:nic => [{
|
57
|
+
:network => inria.networks.find{|n|
|
58
|
+
n['name'] == WAN_NAME
|
59
|
+
}
|
60
|
+
}],
|
61
|
+
:location => inria
|
62
|
+
)
|
63
|
+
aggregator_ip = aggregator['nic'][0]['ip']
|
64
|
+
session.logger.info "AGGREGATOR IP=#{aggregator_ip}"
|
65
|
+
|
66
|
+
session.logger.info "Launching server..."
|
67
|
+
# Find an existing server in the experiment, or set up a new one:
|
68
|
+
server = experiment.computes.find{|vm|
|
69
|
+
vm['name'] == "server-experiment#{experiment['id']}"
|
70
|
+
} || experiment.computes.submit(
|
71
|
+
:name => "server-experiment#{experiment['id']}",
|
72
|
+
:instance_type => "small",
|
73
|
+
:disk => [{
|
74
|
+
:storage => hlrs.storages.find{|s|
|
75
|
+
s['name'] == SERVER_IMAGE_NAME
|
76
|
+
},
|
77
|
+
:type => "OS"
|
78
|
+
}],
|
79
|
+
:nic => [
|
80
|
+
{:network => hlrs.networks.find{|n| n['name'] == WAN_NAME}}
|
81
|
+
],
|
82
|
+
:location => hlrs,
|
83
|
+
:context => {
|
84
|
+
'aggregator_ip' => aggregator_ip,
|
85
|
+
# Register metric on the server
|
86
|
+
'metrics' => XML::Node.new_cdata('<metric>iperf.server-bw,fgrep ",-" /root/iperf_server_log.txt | cut -d "," -f9 | tail -1</metric>')
|
87
|
+
}
|
88
|
+
)
|
89
|
+
server_ip = server['nic'][0]['ip']
|
90
|
+
session.logger.info "SERVER IP=#{server_ip}"
|
91
|
+
|
92
|
+
# Procedure to create a client VM in a round-robin fashion on each location:
|
93
|
+
def create_client(session, experiment, locations, context = {})
|
94
|
+
# Sort locations by number of clients already running
|
95
|
+
placement = locations.sort{|loc1,loc2|
|
96
|
+
loc1[1].size <=> loc2[1].size
|
97
|
+
}.first
|
98
|
+
location, vms = placement
|
99
|
+
session.logger.info "Deploying client image on #{location['name']}..."
|
100
|
+
vms.push experiment.computes.submit(
|
101
|
+
:name => "#{location['name']}-#{vms.size}-client-e#{experiment['id']}",
|
102
|
+
:instance_type => "small",
|
103
|
+
:disk => [{
|
104
|
+
:storage => location.storages.find{|s|
|
105
|
+
s['name'] == CLIENT_IMAGE_NAME
|
106
|
+
}, :type => "OS"
|
107
|
+
}],
|
108
|
+
:nic => [
|
109
|
+
{:network => location.networks.find{|n| n['name'] == WAN_NAME}}
|
110
|
+
],
|
111
|
+
:location => location,
|
112
|
+
:context => context.merge(
|
113
|
+
'metrics' => XML::Node.new_cdata('<metric>iperf.client-bw,fgrep ",-" /root/iperf_clients_log.txt | cut -d "," -f9 | tail -1</metric>')
|
114
|
+
)
|
115
|
+
)
|
116
|
+
vms.last
|
117
|
+
end
|
118
|
+
|
119
|
+
# Select clients that were potentially already existing
|
120
|
+
# (in case of an experiment reuse).
|
121
|
+
locations.each do |(location,vms)|
|
122
|
+
vms.push(*location.computes.select{|vm|
|
123
|
+
vm['name'] =~ /client-e#{experiment['id']}$/
|
124
|
+
})
|
125
|
+
end
|
126
|
+
clients = locations.map{|(l,vms)| vms}.flatten
|
127
|
+
|
128
|
+
# Create two client if no existing clients:
|
129
|
+
clients = 2.times.map{ create_client(session, experiment, locations, {
|
130
|
+
'aggregator_ip' => aggregator_ip,
|
131
|
+
'iperf_server' => server_ip
|
132
|
+
})} if clients.empty?
|
133
|
+
|
134
|
+
# Pass the experiment status to running.
|
135
|
+
# If it was already running this has no effect.
|
136
|
+
experiment.update(:status => "running")
|
137
|
+
|
138
|
+
# Wait until all VMs are ACTIVE and ssh-able.
|
139
|
+
# Fail if one of them has FAILED.
|
140
|
+
until [aggregator, server, *clients].all?{|vm|
|
141
|
+
vm.reload['state'] == 'ACTIVE' && vm.ssh.accessible?
|
142
|
+
} do
|
143
|
+
fail "One of the VM has failed" if [aggregator, server, *clients].any?{|vm| vm['state'] == 'FAILED'}
|
144
|
+
session.logger.info "One of the VMs is not ready. Waiting..."
|
145
|
+
sleep 20
|
146
|
+
end
|
147
|
+
|
148
|
+
session.logger.info "VMs are now READY!"
|
149
|
+
# Display VM IPs
|
150
|
+
session.logger.info "*** Aggregator IP: #{aggregator_ip}"
|
151
|
+
session.logger.info "*** Server IP: #{server['nic'][0]['ip']}"
|
152
|
+
session.logger.info "*** Client IPs: #{clients.map{|vm| vm['nic'][0]['ip']}.inspect}"
|
153
|
+
|
154
|
+
# Control loop, until the experiment is done.
|
155
|
+
until ['terminated', 'canceled'].include?(experiment.reload['status']) do
|
156
|
+
case experiment['status']
|
157
|
+
when 'running'
|
158
|
+
session.logger.info "Experiment is running. Monitoring elasticity rule..."
|
159
|
+
session.logger.info "Clients: #{locations.map{|(l,vms)| "#{l['name']}: #{vms.map{|vm| vm['name']}.inspect}"}.join("; ")}."
|
160
|
+
|
161
|
+
# Check a metric values:
|
162
|
+
values = experiment.zabbix.metric('system.cpu.util[,system,avg1]', :type => :numeric, :hosts => server).values
|
163
|
+
avg3, avg5 = [values[0..3].avg, values[0..5].avg]
|
164
|
+
|
165
|
+
session.logger.info "Metric: values=#{values.inspect}, avg3=#{avg3}, avg5=#{avg5}."
|
166
|
+
clients_count = locations.map{|(l,vms)| vms}.flatten.length
|
167
|
+
|
168
|
+
# Here if the CPU usage of the iperf server is too low, we'll spawn a
|
169
|
+
# new client. If it's too high, we'll shut a client down.
|
170
|
+
if clients_count <= 10 && values.length >= 3 && avg3 <= 20
|
171
|
+
session.logger.warn "Scaling UP (avg=#{avg3})!"
|
172
|
+
vm = create_client(session, experiment, locations, {
|
173
|
+
'aggregator_ip' => aggregator_ip,
|
174
|
+
'iperf_server' => server_ip
|
175
|
+
})
|
176
|
+
sleep(10) until vm.ssh.accessible?
|
177
|
+
elsif clients_count > 1 && values.length >= 5 && avg5 >= 22
|
178
|
+
session.logger.warn "Scaling DOWN (avg=#{avg5})!"
|
179
|
+
# Delete the first client of the location which has the most clients:
|
180
|
+
locations.sort{|loc1,loc2|
|
181
|
+
loc2[1].size <=> loc1[1].size
|
182
|
+
}.first[1].shift.delete
|
183
|
+
else
|
184
|
+
session.logger.info "Nothing to do."
|
185
|
+
end
|
186
|
+
|
187
|
+
sleep 60
|
188
|
+
when 'terminating'
|
189
|
+
session.logger.info "Experiment is terminating. Here you could save images, retrieve data, etc."
|
190
|
+
sleep 30
|
191
|
+
else
|
192
|
+
session.logger.info "Experiment is #{experiment['status']}. Nothing to do yet."
|
193
|
+
sleep 60
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
session.logger.warn "Experiment terminated!"
|
198
|
+
|
199
|
+
rescue Exception => e
|
200
|
+
session.logger.error "#{e.class.name}: #{e.message}"
|
201
|
+
session.logger.error e.backtrace.join("\n")
|
202
|
+
session.logger.warn "Cleaning up in 30 seconds. Hit CTRL-C now to keep your VMs..."
|
203
|
+
sleep 30
|
204
|
+
experiment.delete unless experiment.nil?
|
205
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'restfully'
|
3
|
+
require 'restfully/addons/bonfire'
|
4
|
+
|
5
|
+
CLIENT_IMAGE_NAME = "BonFIRE Debian Squeeze 2G v2"
|
6
|
+
SERVER_IMAGE_NAME = "BonFIRE Debian Squeeze 2G v2"
|
7
|
+
AGGREGATOR_IMAGE_NAME = "BonFIRE Zabbix Aggregator v4"
|
8
|
+
WAN_NAME = "BonFIRE WAN"
|
9
|
+
|
10
|
+
session = Restfully::Session.new(
|
11
|
+
:configuration_file => "~/.restfully/api.bonfire-project.eu",
|
12
|
+
:cache => false,
|
13
|
+
:gateway => "ssh.bonfire.grid5000.fr",
|
14
|
+
:keys => ["~/.ssh/id_rsa"]
|
15
|
+
)
|
16
|
+
session.logger.level = Logger::INFO
|
17
|
+
|
18
|
+
experiment = nil
|
19
|
+
|
20
|
+
begin
|
21
|
+
# Find an existing running experiment with the same name or submit a new
|
22
|
+
# one. This allows re-using an experiment when developing a new script.
|
23
|
+
experiment = session.root.experiments.find{|e|
|
24
|
+
e['name'] == "Demo VW" && e['status'] == "running"
|
25
|
+
} || session.root.experiments.submit(
|
26
|
+
:name => "Demo VW",
|
27
|
+
:description => "VW demo using Restfully - #{Time.now.to_s}",
|
28
|
+
:status => "waiting",
|
29
|
+
:walltime => 8*3600 # 8 hours
|
30
|
+
)
|
31
|
+
|
32
|
+
# Create shortcuts for location resources:
|
33
|
+
inria = session.root.locations[:'fr-inria']
|
34
|
+
fail "Can't select the fr-inria location" if inria.nil?
|
35
|
+
ibbt = session.root.locations[:'be-ibbt']
|
36
|
+
fail "Can't select the de-hlrs location" if ibbt.nil?
|
37
|
+
|
38
|
+
# In this array we'll store the clients launched at each site:
|
39
|
+
locations = [[ibbt,[]]]
|
40
|
+
|
41
|
+
private_network = experiment.networks.submit(
|
42
|
+
:location => ibbt,
|
43
|
+
:name => "network-experiment#{experiment['id']}",
|
44
|
+
:bandwidth => 1000,
|
45
|
+
:latency => 0,
|
46
|
+
:size => 24,
|
47
|
+
:lossrate => 0,
|
48
|
+
# You MUST specify the address:
|
49
|
+
:address => "192.168.0.0"
|
50
|
+
)
|
51
|
+
|
52
|
+
session.logger.info "Launching aggregator..."
|
53
|
+
aggregator = experiment.computes.find{|vm|
|
54
|
+
vm['name'] == "BonFIRE-monitor-experiment#{experiment['id']}"
|
55
|
+
} || experiment.computes.submit(
|
56
|
+
:name => "BonFIRE-monitor-experiment#{experiment['id']}",
|
57
|
+
:instance_type => "small",
|
58
|
+
:disk => [
|
59
|
+
{
|
60
|
+
:storage => inria.storages.find{|s|
|
61
|
+
s['name'] == AGGREGATOR_IMAGE_NAME
|
62
|
+
},
|
63
|
+
:type => "OS"
|
64
|
+
}
|
65
|
+
],
|
66
|
+
:nic => [
|
67
|
+
{
|
68
|
+
:network => inria.networks.find{|n|
|
69
|
+
n['name'] == WAN_NAME
|
70
|
+
}
|
71
|
+
}
|
72
|
+
],
|
73
|
+
:location => inria
|
74
|
+
)
|
75
|
+
aggregator_ip = aggregator['nic'][0]['ip']
|
76
|
+
session.logger.info "AGGREGATOR IP=#{aggregator_ip}"
|
77
|
+
|
78
|
+
session.logger.info "Launching server..."
|
79
|
+
# Set up server
|
80
|
+
server = experiment.computes.find{|vm|
|
81
|
+
vm['name'] == "server-experiment#{experiment['id']}"
|
82
|
+
} || experiment.computes.submit(
|
83
|
+
:name => "server-experiment#{experiment['id']}",
|
84
|
+
:instance_type => "small",
|
85
|
+
:disk => [
|
86
|
+
{
|
87
|
+
:storage => ibbt.storages.find{|s|
|
88
|
+
s['name'] == SERVER_IMAGE_NAME
|
89
|
+
},
|
90
|
+
:type => "OS"
|
91
|
+
}
|
92
|
+
],
|
93
|
+
:nic => [
|
94
|
+
{:network => ibbt.networks.find{|n| n['name'] == WAN_NAME}},
|
95
|
+
{:network => private_network, :ip => '192.168.0.2'}
|
96
|
+
],
|
97
|
+
:location => ibbt,
|
98
|
+
:context => {
|
99
|
+
'aggregator_ip' => aggregator_ip,
|
100
|
+
# Register metric on the server
|
101
|
+
'metrics' => XML::Node.new_cdata('<metric>iperf.server-bw,fgrep ",-" /root/iperf_server_log.txt | cut -d "," -f9 | tail -1</metric>')
|
102
|
+
}
|
103
|
+
)
|
104
|
+
server_ip = server['nic'][0]['ip']
|
105
|
+
session.logger.info "SERVER IP=#{server_ip}"
|
106
|
+
|
107
|
+
# Procedure to create a client VM in a round-robin fashion on each location:
|
108
|
+
def create_client(session, experiment, locations, context = {})
|
109
|
+
# Sort locations by number of clients already running
|
110
|
+
placement = locations.sort{|loc1,loc2|
|
111
|
+
loc1[1].size <=> loc2[1].size
|
112
|
+
}.first
|
113
|
+
location, vms = placement
|
114
|
+
session.logger.info "Deploying client image on #{location['name']}..."
|
115
|
+
private_ip = "192.168.0.#{vms.size+3}"
|
116
|
+
vms.push experiment.computes.submit(
|
117
|
+
:name => "#{location['name']}-#{vms.size}-client-e#{experiment['id']}",
|
118
|
+
:instance_type => "small",
|
119
|
+
:disk => [
|
120
|
+
{
|
121
|
+
:storage => location.storages.find{|s|
|
122
|
+
s['name'] == CLIENT_IMAGE_NAME
|
123
|
+
},
|
124
|
+
:type => "OS"
|
125
|
+
}
|
126
|
+
],
|
127
|
+
:nic => [
|
128
|
+
{:network => location.networks.find{|n| n['name'] == WAN_NAME}},
|
129
|
+
{:network => context.delete(:private_network), :ip => private_ip}
|
130
|
+
],
|
131
|
+
:location => location,
|
132
|
+
:context => context.merge(
|
133
|
+
'metrics' => XML::Node.new_cdata('<metric>iperf.client-bw,fgrep ",-" /root/iperf_clients_log.txt | cut -d "," -f9 | tail -1</metric>')
|
134
|
+
)
|
135
|
+
)
|
136
|
+
vms.last
|
137
|
+
end
|
138
|
+
|
139
|
+
# Select clients that were potentially already existing
|
140
|
+
# (in case of an experiment reuse).
|
141
|
+
locations.each do |(location,vms)|
|
142
|
+
vms.push(*location.computes.select{|vm|
|
143
|
+
vm['name'] =~ /client-e#{experiment['id']}$/
|
144
|
+
})
|
145
|
+
end
|
146
|
+
clients = locations.map{|(l,vms)| vms}.flatten
|
147
|
+
|
148
|
+
# Create two client if no existing clients:
|
149
|
+
clients = 2.times.map{ create_client(session, experiment, locations, {
|
150
|
+
'aggregator_ip' => aggregator_ip,
|
151
|
+
'iperf_server' => '192.168.0.2',
|
152
|
+
:private_network => private_network
|
153
|
+
})} if clients.empty?
|
154
|
+
|
155
|
+
# Pass the experiment status to running.
|
156
|
+
# If it was already running this has no effect.
|
157
|
+
experiment.update(:status => "running")
|
158
|
+
|
159
|
+
# Wait until all VMs are ACTIVE and ssh-able.
|
160
|
+
# Fail if one of them has FAILED.
|
161
|
+
until [aggregator, server, *clients].all?{|vm|
|
162
|
+
vm.reload['state'] == 'ACTIVE' && vm.ssh.accessible?
|
163
|
+
} do
|
164
|
+
fail "One of the VM has failed" if [aggregator, server, *clients].any?{|vm| vm['state'] == 'FAILED'}
|
165
|
+
session.logger.info "One of the VMs is not ready. Waiting..."
|
166
|
+
sleep 20
|
167
|
+
end
|
168
|
+
|
169
|
+
session.logger.info "VMs are now READY!"
|
170
|
+
# Display VM IPs
|
171
|
+
session.logger.info "*** Aggregator IP: #{aggregator_ip}"
|
172
|
+
session.logger.info "*** Server IP: #{server['nic'][0]['ip']}"
|
173
|
+
session.logger.info "*** Client IPs: #{clients.map{|vm| vm['nic'][0]['ip']}.inspect}"
|
174
|
+
|
175
|
+
# Control loop, until the experiment is done.
|
176
|
+
until ['terminated', 'canceled'].include?(experiment.reload['status']) do
|
177
|
+
case experiment['status']
|
178
|
+
when 'running'
|
179
|
+
session.logger.info "Experiment is running..."
|
180
|
+
sleep 60
|
181
|
+
when 'terminating'
|
182
|
+
session.logger.info "Experiment is terminating. Here you could save images, retrieve data, etc."
|
183
|
+
sleep 30
|
184
|
+
else
|
185
|
+
session.logger.info "Experiment is #{experiment['status']}. Nothing to do yet."
|
186
|
+
sleep 60
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
session.logger.warn "Experiment terminated!"
|
191
|
+
|
192
|
+
rescue Exception => e
|
193
|
+
session.logger.error "#{e.class.name}: #{e.message}"
|
194
|
+
session.logger.error e.backtrace.join("\n")
|
195
|
+
session.logger.warn "Cleaning up in 30 seconds. Hit CTRL-C now to keep your VMs..."
|
196
|
+
sleep 30
|
197
|
+
experiment.delete unless experiment.nil?
|
198
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'restfully'
|
3
|
+
require 'restfully/addons/bonfire'
|
4
|
+
|
5
|
+
SERVER_IMAGE_NAME = "BonFIRE Debian Squeeze v2"
|
6
|
+
WAN_NAME = "BonFIRE WAN"
|
7
|
+
|
8
|
+
logger = Logger.new(STDOUT)
|
9
|
+
logger.level = Logger::INFO
|
10
|
+
|
11
|
+
session = Restfully::Session.new(
|
12
|
+
:configuration_file => "~/.restfully/api.bonfire-project.eu",
|
13
|
+
:gateway => "ssh.bonfire.grid5000.fr",
|
14
|
+
:keys => ["~/.ssh/id_rsa"],
|
15
|
+
:cache => false,
|
16
|
+
:logger => logger
|
17
|
+
)
|
18
|
+
|
19
|
+
experiment = nil
|
20
|
+
|
21
|
+
begin
|
22
|
+
# Find an existing running experiment with the same name or submit a new
|
23
|
+
# one. This allows re-using an experiment when developing a new script.
|
24
|
+
experiment = session.root.experiments.find{|e|
|
25
|
+
e['name'] == "Demo SSH" && e['status'] == "running"
|
26
|
+
} || session.root.experiments.submit(
|
27
|
+
:name => "Demo SSH",
|
28
|
+
:description => "SSH demo using Restfully - #{Time.now.to_s}",
|
29
|
+
:status => "waiting",
|
30
|
+
:walltime => 8*3600 # 8 hours
|
31
|
+
)
|
32
|
+
|
33
|
+
# Create shortcuts for location resources:
|
34
|
+
inria = session.root.locations[:'fr-inria']
|
35
|
+
fail "Can't select the fr-inria location" if inria.nil?
|
36
|
+
|
37
|
+
session.logger.info "Launching VM..."
|
38
|
+
# Find an existing server in the experiment, or set up a new one:
|
39
|
+
server = experiment.computes.find{|vm|
|
40
|
+
vm['name'] == "VM-experiment#{experiment['id']}"
|
41
|
+
} || experiment.computes.submit(
|
42
|
+
:name => "VM-experiment#{experiment['id']}",
|
43
|
+
:instance_type => "small",
|
44
|
+
:disk => [{
|
45
|
+
:storage => inria.storages.find{|s|
|
46
|
+
s['name'] == SERVER_IMAGE_NAME
|
47
|
+
},
|
48
|
+
:type => "OS"
|
49
|
+
}],
|
50
|
+
:nic => [
|
51
|
+
{:network => inria.networks.find{|n| n['name'] == WAN_NAME}}
|
52
|
+
],
|
53
|
+
:location => inria
|
54
|
+
)
|
55
|
+
server_ip = server['nic'][0]['ip']
|
56
|
+
session.logger.info "SERVER IP=#{server_ip}"
|
57
|
+
|
58
|
+
# Pass the experiment status to running.
|
59
|
+
# If it was already running this has no effect.
|
60
|
+
experiment.update(:status => "running")
|
61
|
+
|
62
|
+
# Wait until all VMs are ACTIVE and ssh-able.
|
63
|
+
# Fail if one of them has FAILED.
|
64
|
+
until [server].all?{|vm|
|
65
|
+
vm.reload['state'] == 'ACTIVE' && vm.ssh.accessible?
|
66
|
+
} do
|
67
|
+
fail "One of the VM has failed" if [server].any?{|vm|
|
68
|
+
vm['state'] == 'FAILED'
|
69
|
+
}
|
70
|
+
session.logger.info "One of the VMs is not ready. Waiting..."
|
71
|
+
sleep 20
|
72
|
+
end
|
73
|
+
|
74
|
+
session.logger.info "VMs are now READY!"
|
75
|
+
# Display VM IPs
|
76
|
+
session.logger.info "*** Server IP: #{server['nic'][0]['ip']}"
|
77
|
+
|
78
|
+
server.ssh do |ssh|
|
79
|
+
session.logger.info "Uploading content..."
|
80
|
+
# Here is how you would upload a file:
|
81
|
+
# ssh.scp.upload!("/path/to/file", '/tmp/file.log')
|
82
|
+
# Here is how you can upload some in-memory data:
|
83
|
+
ssh.scp.upload!(StringIO.new('some data'), '/tmp/file.log')
|
84
|
+
# See <http://net-ssh.github.com/scp/v1/api/index.html> for more details.
|
85
|
+
|
86
|
+
session.logger.info "Content of uploaded file:"
|
87
|
+
puts ssh.exec!("cat /tmp/file.log")
|
88
|
+
|
89
|
+
session.logger.info "Installing things..."
|
90
|
+
output = ssh.exec!("apt-get install curl -y")
|
91
|
+
session.logger.debug output
|
92
|
+
|
93
|
+
session.logger.info "Running query against API..."
|
94
|
+
puts ssh.exec!("source /etc/default/bonfire && curl -k $BONFIRE_URI/locations/$BONFIRE_PROVIDER/computes/$BONFIRE_RESOURCE_ID -u $BONFIRE_CREDENTIALS")
|
95
|
+
end
|
96
|
+
|
97
|
+
session.logger.warn "Success! Will delete experiment in 10 seconds. Hit CTRL-C now to keep your VMs..."
|
98
|
+
sleep 10
|
99
|
+
experiment.delete
|
100
|
+
|
101
|
+
rescue Exception => e
|
102
|
+
session.logger.error "#{e.class.name}: #{e.message}"
|
103
|
+
session.logger.error e.backtrace.join("\n")
|
104
|
+
session.logger.warn "Cleaning up in 30 seconds. Hit CTRL-C now to keep your VMs..."
|
105
|
+
sleep 30
|
106
|
+
experiment.delete unless experiment.nil?
|
107
|
+
end
|
@@ -4,11 +4,16 @@ require 'net/sftp'
|
|
4
4
|
|
5
5
|
module Restfully
|
6
6
|
class Resource
|
7
|
+
# Opens an SSH session on the resource's IP.
|
8
|
+
# Returns the result of the last statement of the given block.
|
7
9
|
def ssh(user = nil, opts = {}, &block)
|
8
10
|
raise NotImplementedError unless uri.to_s =~ /\/computes\/\w+$/
|
9
11
|
@ssh ||= SSH.new(session, self)
|
10
|
-
|
11
|
-
|
12
|
+
if block
|
13
|
+
@ssh.run(@ssh.ip, user, opts, &block)
|
14
|
+
else
|
15
|
+
@ssh
|
16
|
+
end
|
12
17
|
end
|
13
18
|
end
|
14
19
|
|
@@ -27,10 +32,18 @@ module Restfully
|
|
27
32
|
gateway = session.config[:gateway]
|
28
33
|
if gateway
|
29
34
|
gateway_handler = Net::SSH::Gateway.new(gateway, session.config[:username], options)
|
30
|
-
|
35
|
+
result = nil
|
36
|
+
gateway_handler.ssh(fqdn, user, options) {|handler|
|
37
|
+
result = block.call(handler)
|
38
|
+
}
|
31
39
|
gateway_handler.shutdown!
|
40
|
+
result
|
32
41
|
else
|
33
|
-
|
42
|
+
result = nil
|
43
|
+
Net::SSH.start(fqdn, user, options) {|handler|
|
44
|
+
result = block.call(handler)
|
45
|
+
}
|
46
|
+
result
|
34
47
|
end
|
35
48
|
end
|
36
49
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: restfully-addons
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,12 +9,12 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-09-21 00:00:00.000000000 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: restfully
|
17
|
-
requirement: &
|
17
|
+
requirement: &2153539680 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: '0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *2153539680
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: net-ssh-gateway
|
28
|
-
requirement: &
|
28
|
+
requirement: &2153570680 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ! '>='
|
@@ -33,10 +33,10 @@ dependencies:
|
|
33
33
|
version: '0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *2153570680
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: net-scp
|
39
|
-
requirement: &
|
39
|
+
requirement: &2153570260 !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
41
41
|
requirements:
|
42
42
|
- - ! '>='
|
@@ -44,10 +44,10 @@ dependencies:
|
|
44
44
|
version: '0'
|
45
45
|
type: :runtime
|
46
46
|
prerelease: false
|
47
|
-
version_requirements: *
|
47
|
+
version_requirements: *2153570260
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: net-sftp
|
50
|
-
requirement: &
|
50
|
+
requirement: &2153569840 !ruby/object:Gem::Requirement
|
51
51
|
none: false
|
52
52
|
requirements:
|
53
53
|
- - ! '>='
|
@@ -55,10 +55,10 @@ dependencies:
|
|
55
55
|
version: '0'
|
56
56
|
type: :runtime
|
57
57
|
prerelease: false
|
58
|
-
version_requirements: *
|
58
|
+
version_requirements: *2153569840
|
59
59
|
- !ruby/object:Gem::Dependency
|
60
60
|
name: net-ssh-multi
|
61
|
-
requirement: &
|
61
|
+
requirement: &2153569420 !ruby/object:Gem::Requirement
|
62
62
|
none: false
|
63
63
|
requirements:
|
64
64
|
- - ! '>='
|
@@ -66,10 +66,10 @@ dependencies:
|
|
66
66
|
version: '0'
|
67
67
|
type: :runtime
|
68
68
|
prerelease: false
|
69
|
-
version_requirements: *
|
69
|
+
version_requirements: *2153569420
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: libxml-ruby
|
72
|
-
requirement: &
|
72
|
+
requirement: &2153569000 !ruby/object:Gem::Requirement
|
73
73
|
none: false
|
74
74
|
requirements:
|
75
75
|
- - ! '>='
|
@@ -77,10 +77,10 @@ dependencies:
|
|
77
77
|
version: '0'
|
78
78
|
type: :runtime
|
79
79
|
prerelease: false
|
80
|
-
version_requirements: *
|
80
|
+
version_requirements: *2153569000
|
81
81
|
- !ruby/object:Gem::Dependency
|
82
82
|
name: rake
|
83
|
-
requirement: &
|
83
|
+
requirement: &2153568500 !ruby/object:Gem::Requirement
|
84
84
|
none: false
|
85
85
|
requirements:
|
86
86
|
- - ~>
|
@@ -88,7 +88,7 @@ dependencies:
|
|
88
88
|
version: '0.8'
|
89
89
|
type: :development
|
90
90
|
prerelease: false
|
91
|
-
version_requirements: *
|
91
|
+
version_requirements: *2153568500
|
92
92
|
description: Addons for Restfully
|
93
93
|
email:
|
94
94
|
- cyril.rohr@inria.fr
|
@@ -103,6 +103,9 @@ files:
|
|
103
103
|
- lib/restfully/addons/bonfire.rb
|
104
104
|
- lib/restfully/addons/version.rb
|
105
105
|
- lib/restfully/addons.rb
|
106
|
+
- examples/bonfire/scenario1.rb
|
107
|
+
- examples/bonfire/scenario2.rb
|
108
|
+
- examples/bonfire/ssh.rb
|
106
109
|
- Rakefile
|
107
110
|
- LICENSE
|
108
111
|
- README.md
|