rscalr 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.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ rscalr
2
+ ======
3
+
4
+ Ruby Scalr API implementation
data/lib/rscalr.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'rscalr/api/scalr'
2
+ require 'rscalr/model/dashboard'
3
+ require 'rscalr/model/farm'
4
+ require 'rscalr/model/farm_role'
5
+ require 'rscalr/model/role'
6
+ require 'rscalr/model/script'
7
+ require 'rscalr/model/server'
@@ -0,0 +1,150 @@
1
+ require 'openssl'
2
+ require 'net/http'
3
+ require 'cgi'
4
+ require 'rexml/document'
5
+
6
+ class Scalr
7
+ attr_accessor :config
8
+
9
+ # Default versions
10
+ $DEFAULT_VERSION = '2.3.0'
11
+ $DEFAULT_AUTH_VERSION = '3'
12
+
13
+ def initialize(config)
14
+ @config = config
15
+ @config[:version] = $DEFAULT_VERSION if @config[:version].nil?
16
+ @config[:auth_version] = $DEFAULT_AUTH_VERSION if @config[:auth_version].nil?
17
+ end
18
+
19
+ def generate_sig(action, timestamp)
20
+ message = action + ':' + @config[:key_id] + ':' + timestamp
21
+ hexdigest = OpenSSL::HMAC.hexdigest('sha256', @config[:key_secret], message)
22
+ [[hexdigest].pack("H*")].pack("m0")
23
+ end
24
+
25
+ def execute_api_call(action, action_params=nil)
26
+
27
+ begin
28
+ params = { :Action => action, :TimeStamp => Time.now.strftime("%Y-%m-%dT%H:%M:%SZ") }
29
+ params.merge!(action_params) unless action_params.nil?
30
+
31
+ params[:Signature] = generate_sig(action, params[:TimeStamp])
32
+ params[:Version] = @config[:version]
33
+ params[:AuthVersion] = @config[:auth_version]
34
+ params[:KeyID] = @config[:key_id]
35
+
36
+ uri = URI("https://api.scalr.net/?" + hash_to_querystring(params))
37
+
38
+ response = Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http|
39
+ request = Net::HTTP::Get.new uri.request_uri
40
+ http.request request # Net::HTTPResponse object
41
+ end
42
+
43
+ case response
44
+ when Net::HTTPSuccess then
45
+ result = ScalrResponse.new response.body
46
+ else
47
+ result = build_error_response(response)
48
+ end
49
+
50
+ rescue => ex
51
+ result = build_error_response(ex.message)
52
+ end
53
+
54
+ result
55
+ end
56
+
57
+ def build_error_response(message)
58
+ result = ScalrResponse.new "<?xml version='1.0?>"
59
+ result.add_element("Error")
60
+ ele = REXML::Element.new "TransactionID"
61
+ ele.text = generate_sig(message, Time.now.strftime("%Y-%m-%dT%H:%M:%SZ"))
62
+ result.root.elements << ele
63
+ ele = REXML::Element.new "Message"
64
+ ele.text = message
65
+ result.root.elements << ele
66
+
67
+ result
68
+ end
69
+
70
+ def hash_to_querystring(h)
71
+ h.map{|k,v| "#{k.to_s}=#{CGI::escape(v.to_s)}"}.join('&')
72
+ end
73
+
74
+
75
+ #=================== API Section ===================================
76
+
77
+ def farms_list
78
+ execute_api_call('FarmsList')
79
+ end
80
+
81
+ def scripts_list
82
+ execute_api_call('ScriptsList')
83
+ end
84
+
85
+ def farm_get_details(farm_id)
86
+ execute_api_call('FarmGetDetails', { :FarmID => farm_id })
87
+ end
88
+
89
+ def script_execute(farm_id, script_id, timeout=30, async=:no_async, farm_role_id=nil, server_id=nil, revsion=nil, config_vars=nil)
90
+ async_int = 0
91
+ async_int = 1 if async == :async
92
+
93
+ params = { :FarmID => farm_id, :ScriptID => script_id, :Timeout => timeout, :Async => async_int }
94
+ params[:FarmRoleID] = farm_role_id unless farm_role_id.nil?
95
+ params[:ServerID] = server_id unless server_id.nil?
96
+ params[:Revision] = revsion unless revsion.nil?
97
+ if config_vars != nil
98
+ config_vars.each {|key, value|
99
+ params["ConfigVariables[#{CGI::escape(key)}]"] = value
100
+ }
101
+ end
102
+
103
+ execute_api_call('ScriptExecute', params)
104
+ end
105
+
106
+ def script_get_details(script_id)
107
+ execute_api_call('ScriptGetDetails', { :ScriptID => script_id })
108
+ end
109
+
110
+ def farm_role_parameters_list(farm_role_id)
111
+ execute_api_call('FarmRoleParametersList', { :FarmRoleID => farm_role_id })
112
+ end
113
+
114
+ def farm_role_update_parameter_value(farm_role_id, param_name, param_value)
115
+ execute_api_call('FarmRoleUpdateParameterValue', { :FarmRoleID => farm_role_id, :ParamName => param_name, :ParamValue => param_value })
116
+ end
117
+
118
+ def scripting_logs_list(farm_id, server_id=nil, start=nil, limit=nil)
119
+ params = { :FarmID => farm_id }
120
+ params[:ServerID] = server_id unless server_id.nil?
121
+ params[:StartFrom] = start unless start.nil?
122
+ params[:RecordsLimit] = limit unless limit.nil?
123
+
124
+ execute_api_call('ScriptingLogsList', params)
125
+ end
126
+ end
127
+
128
+ class ScalrResponse < REXML::Document
129
+
130
+ def initialize http_response_body=nil
131
+ if http_response_body.nil?
132
+ super
133
+ else
134
+ super http_response_body
135
+ end
136
+ end
137
+
138
+ def success?
139
+ root.name != 'Error'
140
+ end
141
+
142
+ def error_message
143
+ if success?
144
+ nil
145
+ else
146
+ root.elements['Message'].text
147
+ end
148
+ end
149
+ end
150
+
@@ -0,0 +1,69 @@
1
+ require 'rscalr/model/farm'
2
+ require 'rscalr/model/script'
3
+
4
+ class Dashboard
5
+
6
+ def initialize client
7
+ @client = client
8
+ end
9
+
10
+ def get_farm(name)
11
+ if @farms.nil?
12
+ load_farms
13
+ end
14
+ @farms[name]
15
+ end
16
+
17
+ def load_farms
18
+ @farms = {} if @farms == nil
19
+
20
+ scalr_response = @client.farms_list
21
+ scalr_response.root.each_element('FarmSet/Item') { |row|
22
+ farm = Farm.new @client
23
+
24
+ row.each_element { |prop|
25
+ if "ID" == prop.name
26
+ farm.id = prop.text
27
+ elsif "Name" == prop.name
28
+ farm.name = prop.text
29
+ elsif "Status" == prop.name
30
+ farm.status = prop.text
31
+ end
32
+ }
33
+ @farms[farm.name] = farm
34
+ }
35
+
36
+ @farms
37
+ end
38
+
39
+ def get_script(name)
40
+ if @scripts.nil?
41
+ load_scripts
42
+ end
43
+ @scripts[name]
44
+ end
45
+
46
+ def load_scripts
47
+ @scripts = {}
48
+
49
+ scalr_response = @client.scripts_list
50
+ scalr_response.root.each_element('ScriptSet/Item') { |row|
51
+ script = Script.new @client
52
+
53
+ row.each_element { |prop|
54
+ if "ID" == prop.name
55
+ script.id = prop.text
56
+ elsif "Name" == prop.name
57
+ script.name = prop.text
58
+ elsif "Description" == prop.name
59
+ script.description = prop.text
60
+ elsif "LatestRevision" == prop.name
61
+ script.latest_revision = prop.text.to_i
62
+ end
63
+ }
64
+ @scripts[script.name] = script
65
+ }
66
+
67
+ @scripts
68
+ end
69
+ end
@@ -0,0 +1,64 @@
1
+ class Farm
2
+ attr_accessor :id, :name, :status
3
+
4
+ def initialize client
5
+ @client = client
6
+ end
7
+
8
+ def get_role(name)
9
+ if @farm_roles.nil?
10
+ load_details
11
+ end
12
+ @farm_roles[name]
13
+ end
14
+
15
+ def load_details
16
+ @farm_roles = {}
17
+
18
+ scalr_response = @client.farm_get_details @id
19
+ scalr_response.root.each_element('FarmRoleSet/Item') { |row|
20
+ role = FarmRole.new
21
+
22
+ row.each_element { |prop|
23
+ if "ID" == prop.name
24
+ role.farm_role_id = prop.text
25
+ elsif "RoleID" == prop.name
26
+ role.role_id = prop.text
27
+ elsif "Name" == prop.name
28
+ role.name = prop.text
29
+ elsif "ServerSet" == prop.name
30
+ prop.each_element { |server_element|
31
+ server = Server.new
32
+ server.role = role
33
+
34
+ server_element.each_element { |server_prop|
35
+ if "ServerID" == prop.name
36
+ server.server_id = prop.text
37
+ elsif "ExternalIP" == prop.name
38
+ server.external_ip = prop.text
39
+ elsif "InternalIP" == prop.name
40
+ server.external_ip = prop.text
41
+ elsif "Status" == prop.name
42
+ server.status = prop.text
43
+ elsif "Index" == prop.name
44
+ server.index = prop.text.to_i
45
+ elsif "Uptime" == prop.name
46
+ server.uptime = prop.text.to_f
47
+ end
48
+ }
49
+ role.servers << server
50
+ }
51
+ end
52
+ }
53
+
54
+ @farm_roles[role.name] = role
55
+ }
56
+
57
+ @farm_roles
58
+ end
59
+
60
+ def to_s
61
+ "{ type: \"farm\", id: #{@id}, name: \"#{@name}\", status: #{@status} }"
62
+ end
63
+ end
64
+
@@ -0,0 +1,14 @@
1
+ require 'rscalr/model/role'
2
+
3
+ class FarmRole < Role
4
+ attr_accessor :farm_role_id, :servers
5
+
6
+ def initialize
7
+ @servers = []
8
+ end
9
+
10
+ def to_s
11
+ "{ type: \"farm_role\", farm_role_id: #{@farm_role_id}, role_id: #{@role_id}, name: \"#{@name}\", num_servers: #{@servers.size} }"
12
+ end
13
+
14
+ end
@@ -0,0 +1,7 @@
1
+ class Role
2
+ attr_accessor :role_id, :name
3
+
4
+ def to_s
5
+ "{ type: \"role\", role_id: #{@role_id}, name: \"#{@name}\" }"
6
+ end
7
+ end
@@ -0,0 +1,88 @@
1
+ require 'time'
2
+
3
+ class Script
4
+ attr_accessor :id, :name, :description, :latest_revision, :details
5
+
6
+ def initialize client
7
+ @client = client
8
+ end
9
+
10
+ def execute(farm_id, timeout=30, async=:no_async, farm_role_id=nil, server_id=nil, revision=nil, config_vars=nil)
11
+ set_revision(revision) # Side effect!
12
+ @client.script_execute farm_id, @id, timeout, async, farm_role_id, server_id, @revision, config_vars
13
+ end
14
+
15
+ def load_details
16
+ @details = ScriptDetails.new @id
17
+ scalr_response = @client.script_get_details @id
18
+ scalr_response.root.each_element('ScriptRevisionSet/Item') { |row|
19
+ revision = ScriptRevision.new
20
+
21
+ row.each_element { |prop|
22
+ if "Revision" == prop.name
23
+ revision.revision = prop.text.to_i
24
+ elsif "Date" == prop.name
25
+ revision.date = Time.parse(prop.text)
26
+ elsif "ConfigVariables" == prop.name
27
+ prop.each_element { |configvar_element|
28
+ configvar_element.each_element { |configvar_prop|
29
+ if "Name" == prop.name
30
+ revision.config_variables << prop.text
31
+ end
32
+ }
33
+ }
34
+ end
35
+ }
36
+
37
+ @details.revisions[revision.revision] = revision
38
+ }
39
+
40
+ @details
41
+ end
42
+
43
+ def get_config_variables
44
+ load_details unless @details != nil
45
+ @revision = @latest_revision unless @revision != nil
46
+ puts "Rev: #{@revision} -- Deets: #{@details}"
47
+ @details.revisions[@revision].config_variables
48
+ end
49
+
50
+ def set_revision(revision)
51
+ if revision.nil? || revision >= @latest_revision
52
+ @revision = @latest_revision
53
+ elsif revision < 1
54
+ @revision = 1
55
+ else
56
+ @revision = revision
57
+ end
58
+ end
59
+
60
+ def to_s
61
+ "{ type: \"script\", id: #{@id}, name: \"#{@name}\" }"
62
+ end
63
+ end
64
+
65
+ class ScriptDetails
66
+ attr_accessor :id, :revisions
67
+
68
+ def initialize id
69
+ @id = id
70
+ @revisions = {}
71
+ end
72
+
73
+ def to_s
74
+ "{ type: \"script-details\", script_id: #{@id}, num_revisions: #{@revisions.size}}"
75
+ end
76
+ end
77
+
78
+ class ScriptRevision
79
+ attr_accessor :revision, :date, :config_variables
80
+
81
+ def initialize
82
+ @config_variables = []
83
+ end
84
+
85
+ def to_s
86
+ "{ type: \"script-revision\", revision: #{@revision}, date: \"#{@date}\", vars: \"#{@config_variables}\"}"
87
+ end
88
+ end
@@ -0,0 +1,7 @@
1
+ class Server
2
+ attr_accessor :server_id, :external_ip, :internal_ip, :status, :index, :uptime, :role
3
+
4
+ def to_s
5
+ "{ type: \"server\", id: \"#{@server_id}\", external_ip: \"#{@external_ip}\", internal_ip: \"#{@internal_ip}\", role: #{@role} }"
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module Rscalr
2
+ VERSION = "0.0.1"
3
+ end
data/rscalr.gemspec ADDED
@@ -0,0 +1,16 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rscalr/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rscalr"
7
+ s.version = Rscalr::VERSION
8
+ s.authors = ["Nathan Smith"]
9
+ s.email = ["nate@branchout.com"]
10
+ s.summary = %q{Ruby implementation of the Scalr API}
11
+ s.description = %q{Rscalr is a Ruby implementation of the Scalr API, written to interface cleanly with Chef and other internal release management tasks.}
12
+ s.files = `git ls-files`.split("\n")
13
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
15
+ s.require_paths = ["lib"]
16
+ end
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rscalr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Nathan Smith
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-03 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Rscalr is a Ruby implementation of the Scalr API, written to interface
15
+ cleanly with Chef and other internal release management tasks.
16
+ email:
17
+ - nate@branchout.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - .gitignore
23
+ - README.md
24
+ - lib/rscalr.rb
25
+ - lib/rscalr/api/scalr.rb
26
+ - lib/rscalr/model/dashboard.rb
27
+ - lib/rscalr/model/farm.rb
28
+ - lib/rscalr/model/farm_role.rb
29
+ - lib/rscalr/model/role.rb
30
+ - lib/rscalr/model/script.rb
31
+ - lib/rscalr/model/server.rb
32
+ - lib/rscalr/version.rb
33
+ - rscalr.gemspec
34
+ homepage:
35
+ licenses: []
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 1.8.24
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: Ruby implementation of the Scalr API
58
+ test_files: []