restfully-addons 1.0.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/LICENSE ADDED
@@ -0,0 +1 @@
1
+ TODO
data/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # restfully-addons
2
+
3
+ Some additional helpers for RESTful APIs, to deal with SSH connections or
4
+ other protocols.
5
+
6
+ Long-term goal is to add support for these protocols into Restfully.
7
+
8
+ ## Installation
9
+
10
+ $ gem install restfully-addons
11
+
12
+ ## Usage
13
+
14
+ Just require the library you need in your scripts. For instance, requiring
15
+ addons for the BonFIRE API would be done as follows:
16
+
17
+ require 'restfully'
18
+ require 'restfully/addons/bonfire'
19
+
20
+ # your code
21
+
22
+ ## Authors
23
+
24
+ * Cyril Rohr <cyril.rohr@inria.fr>
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ # TODO: test
@@ -0,0 +1,62 @@
1
+ require 'net/ssh/gateway'
2
+ require 'net/scp'
3
+ require 'net/sftp'
4
+
5
+ module Restfully
6
+ class Resource
7
+ def ssh(user = nil, opts = {}, &block)
8
+ raise NotImplementedError unless uri.to_s =~ /\/computes\/\w+$/
9
+ @ssh ||= SSH.new(session, self)
10
+ @ssh.run(@ssh.ip, user, opts, &block) if block
11
+ @ssh
12
+ end
13
+ end
14
+
15
+ class SSH
16
+ attr_reader :session, :resource
17
+
18
+ def initialize(session, resource)
19
+ @session = session
20
+ @resource = resource
21
+ end
22
+
23
+ def run(fqdn, user, options = {}, &block)
24
+ user ||= 'root'
25
+ session.logger.info "Trying to SSH into #{user}@#{fqdn}..."
26
+ options[:keys] ||= session.config[:keys]
27
+ gateway = session.config[:gateway]
28
+ if gateway
29
+ gateway_handler = Net::SSH::Gateway.new(gateway, session.config[:username], options)
30
+ gateway_handler.ssh(fqdn, user, options, &block)
31
+ gateway_handler.shutdown!
32
+ else
33
+ Net::SSH.start(fqdn, user, options, &block)
34
+ end
35
+ end
36
+
37
+ def ip
38
+ @ip ||= accessible?
39
+ end
40
+
41
+ def accessible?
42
+ return false if resource['nic'].empty?
43
+ accessible_nic = resource['nic'].find{|nic|
44
+ ip = nic['ip']
45
+ if ip.nil? || ip =~ /^192.168/
46
+ true
47
+ else
48
+ begin
49
+ Timeout.timeout(10) do
50
+ run(ip, 'root') {|s| s.exec!("hostname") }
51
+ end
52
+ true
53
+ rescue Exception => e
54
+ session.logger.info "Can't SSH yet to #{ip}. Reason: #{e.class.name}, #{e.message}."
55
+ false
56
+ end
57
+ end
58
+ }
59
+ accessible_nic && accessible_nic['ip']
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,130 @@
1
+ require 'json'
2
+
3
+ class Array
4
+ def sum
5
+ inject(:+)
6
+ end
7
+
8
+ def avg
9
+ sum.to_f / size
10
+ end
11
+
12
+ def median
13
+ sorted = sort
14
+ (sorted[size/2] + sorted[(size+1)/2]) / 2
15
+ end
16
+ end
17
+
18
+ module Restfully
19
+ class Resource
20
+ def zabbix
21
+ raise NotImplementedError unless uri.to_s =~ /\/experiments\/[0-9]+$/
22
+ @zabbix ||= Zabbix.new(session, self)
23
+ end
24
+ end
25
+
26
+ class Zabbix
27
+ attr_reader :session, :experiment
28
+ def initialize(session, experiment, opts = {})
29
+ @session = session
30
+ @username = opts[:username] || "Admin"
31
+ @password = opts[:password] || "zabbix"
32
+ @experiment = experiment
33
+ @token, @request_id = nil, 0
34
+ @uri = @experiment.uri.to_s+"/zabbix"
35
+ @max_attempts = 5
36
+ end
37
+
38
+ def request(method, params = {})
39
+ begin
40
+ authenticate if @token.nil? && method != "user.authenticate"
41
+ @request_id += 1
42
+ q = { "jsonrpc" => "2.0", "auth" => @token, "id" => @request_id,
43
+ "method" => method, "params" => params }
44
+ resource = @session.post(@uri,
45
+ JSON.dump(q),
46
+ :head => {
47
+ 'Content-Type' => 'application/json',
48
+ 'Accept' => 'application/json'
49
+ }
50
+ )
51
+
52
+ # That fucking zabbix API returns "text/plain" as content-type...
53
+ h = JSON.parse(resource.response.body)
54
+
55
+ if h['error']
56
+ if h['error']['data'] == "Not authorized"
57
+ @token = nil
58
+ request(method, params)
59
+ else
60
+ raise StandardError, "Received error: #{h.inspect}" if h['error']
61
+ end
62
+ else
63
+ h['result']
64
+ end
65
+ rescue Restfully::HTTP::Error => e
66
+ # retry ad vitam eternam
67
+ sleep 5
68
+ retry
69
+ end
70
+ end
71
+
72
+ def authenticate
73
+ @token = request("user.authenticate", {"user" => @username, "password" => @password})
74
+ end
75
+
76
+ def metric(name, options = {})
77
+ hosts = [options.delete(:hosts) || []].flatten.map{|h|
78
+ [h['name'], h['id']].join("-")
79
+ }
80
+ items = request("item.get", {
81
+ :filter => {
82
+ "host" => hosts[0],
83
+ "key_" => name.to_s
84
+ },
85
+ "output" => "extend"
86
+ }).map{|i| i['itemid']}
87
+
88
+ history = case options[:type]
89
+ when :numeric
90
+ 0
91
+ else
92
+ 1 # STRING
93
+ end
94
+
95
+ # Most recent last
96
+ now = Time.now.to_i
97
+ results = request("history.get", {
98
+ "itemids" => items[0..1],
99
+ # FIX once we can correctly specify metric type
100
+ "history" => history,
101
+ "output" => "extend",
102
+ "time_from" => now-3600,
103
+ "time_till" => now
104
+ })
105
+
106
+ Metric.new(name, results, options)
107
+ end
108
+
109
+ class Metric
110
+ def initialize(name, results, opts = {})
111
+ @name = name
112
+ @results = results
113
+ @opts = opts
114
+ end
115
+
116
+ def values
117
+ @results.map{|r|
118
+ case @opts[:type]
119
+ when :numeric
120
+ r['value'].to_f
121
+ else
122
+ r['value']
123
+ end
124
+ }.reverse
125
+ end
126
+ end
127
+
128
+ end
129
+
130
+ end
@@ -0,0 +1,3 @@
1
+ require 'restfully'
2
+ require 'restfully/addons/bonfire/ssh'
3
+ require 'restfully/addons/bonfire/zabbix'
@@ -0,0 +1,5 @@
1
+ module Restfully
2
+ module Addons
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ require 'restfully/addons/bonfire'
metadata ADDED
@@ -0,0 +1,135 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: restfully-addons
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Cyril Rohr
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-07-29 00:00:00.000000000 +02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: restfully
17
+ requirement: &2156314120 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *2156314120
26
+ - !ruby/object:Gem::Dependency
27
+ name: net-ssh-gateway
28
+ requirement: &2156313660 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *2156313660
37
+ - !ruby/object:Gem::Dependency
38
+ name: net-scp
39
+ requirement: &2156313240 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: *2156313240
48
+ - !ruby/object:Gem::Dependency
49
+ name: net-sftp
50
+ requirement: &2156312820 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :runtime
57
+ prerelease: false
58
+ version_requirements: *2156312820
59
+ - !ruby/object:Gem::Dependency
60
+ name: net-ssh-multi
61
+ requirement: &2156312400 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ type: :runtime
68
+ prerelease: false
69
+ version_requirements: *2156312400
70
+ - !ruby/object:Gem::Dependency
71
+ name: libxml-ruby
72
+ requirement: &2156311980 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ type: :runtime
79
+ prerelease: false
80
+ version_requirements: *2156311980
81
+ - !ruby/object:Gem::Dependency
82
+ name: rake
83
+ requirement: &2156563360 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ~>
87
+ - !ruby/object:Gem::Version
88
+ version: '0.8'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: *2156563360
92
+ description: Addons for Restfully
93
+ email:
94
+ - cyril.rohr@inria.fr
95
+ executables: []
96
+ extensions: []
97
+ extra_rdoc_files:
98
+ - LICENSE
99
+ - README.md
100
+ files:
101
+ - lib/restfully/addons/bonfire/ssh.rb
102
+ - lib/restfully/addons/bonfire/zabbix.rb
103
+ - lib/restfully/addons/bonfire.rb
104
+ - lib/restfully/addons/version.rb
105
+ - lib/restfully/addons.rb
106
+ - Rakefile
107
+ - LICENSE
108
+ - README.md
109
+ has_rdoc: true
110
+ homepage: http://github.com/crohr/restfully-addons
111
+ licenses: []
112
+ post_install_message:
113
+ rdoc_options:
114
+ - --charset=UTF-8
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ! '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '1.8'
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ! '>='
127
+ - !ruby/object:Gem::Version
128
+ version: '1.3'
129
+ requirements: []
130
+ rubyforge_project:
131
+ rubygems_version: 1.6.2
132
+ signing_key:
133
+ specification_version: 3
134
+ summary: Addons for Restfully
135
+ test_files: []