collins_client 0.2.7
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +15 -0
- data/Gemfile.lock +50 -0
- data/README.md +46 -0
- data/Rakefile +66 -0
- data/VERSION +1 -0
- data/collins_client.gemspec +73 -0
- data/lib/collins/address.rb +74 -0
- data/lib/collins/api.rb +119 -0
- data/lib/collins/api/admin.rb +19 -0
- data/lib/collins/api/asset.rb +184 -0
- data/lib/collins/api/asset_state.rb +85 -0
- data/lib/collins/api/attributes.rb +76 -0
- data/lib/collins/api/ip_address.rb +87 -0
- data/lib/collins/api/logging.rb +137 -0
- data/lib/collins/api/management.rb +84 -0
- data/lib/collins/api/tag.rb +46 -0
- data/lib/collins/api/util.rb +28 -0
- data/lib/collins/api/util/errors.rb +45 -0
- data/lib/collins/api/util/parameters.rb +44 -0
- data/lib/collins/api/util/requests.rb +136 -0
- data/lib/collins/api/util/responses.rb +46 -0
- data/lib/collins/asset.rb +311 -0
- data/lib/collins/asset_client.rb +57 -0
- data/lib/collins/client.rb +100 -0
- data/lib/collins/errors.rb +56 -0
- data/lib/collins/ipmi.rb +41 -0
- data/lib/collins/logging.rb +33 -0
- data/lib/collins/monkeypatch.rb +24 -0
- data/lib/collins/option.rb +220 -0
- data/lib/collins/power.rb +99 -0
- data/lib/collins/profile.rb +73 -0
- data/lib/collins/simple_callback.rb +141 -0
- data/lib/collins/state.rb +50 -0
- data/lib/collins/util.rb +145 -0
- data/lib/collins_client.rb +7 -0
- metadata +100 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
module Collins
|
2
|
+
|
3
|
+
class CollinsError < StandardError; end
|
4
|
+
class ExpectationFailedError < CollinsError; end
|
5
|
+
class UnexpectedResponseError < CollinsError; end
|
6
|
+
class RequestError < CollinsError
|
7
|
+
attr_accessor :code, :uri
|
8
|
+
def initialize message, code
|
9
|
+
super(message)
|
10
|
+
@code = code.to_i
|
11
|
+
end
|
12
|
+
def description verbose = false
|
13
|
+
<<-D
|
14
|
+
#{message}
|
15
|
+
Response Code: #{code}
|
16
|
+
URI: #{uri}
|
17
|
+
D
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class RichRequestError < RequestError
|
22
|
+
attr_accessor :class_of, :remote_description, :remote_message, :stacktrace
|
23
|
+
def initialize message, code, description, details = {}
|
24
|
+
super(message, code)
|
25
|
+
@code = code
|
26
|
+
@remote_description = description
|
27
|
+
@class_of = details["classOf"]
|
28
|
+
@remote_message = details["message"]
|
29
|
+
@stacktrace = details["stackTrace"]
|
30
|
+
end
|
31
|
+
def get_remote_stacktrace verbose
|
32
|
+
if verbose then
|
33
|
+
stacktrace
|
34
|
+
else
|
35
|
+
"Suppressed"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
def description verbose = false
|
39
|
+
<<-D
|
40
|
+
#{message}
|
41
|
+
Response Code: #{code}
|
42
|
+
URI: #{uri}
|
43
|
+
Remote Description: #{remote_description}
|
44
|
+
Remote Exception Class: #{class_of}
|
45
|
+
Remote Message:
|
46
|
+
#{remote_message}
|
47
|
+
|
48
|
+
Remote Backtrace:
|
49
|
+
#{get_remote_stacktrace(verbose)}
|
50
|
+
D
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class AuthenticationError < CollinsError; end
|
55
|
+
|
56
|
+
end
|
data/lib/collins/ipmi.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module Collins
|
2
|
+
|
3
|
+
class Ipmi
|
4
|
+
|
5
|
+
include Collins::Util
|
6
|
+
|
7
|
+
attr_accessor :address, :asset_id, :gateway, :id, :netmask, :password, :username
|
8
|
+
|
9
|
+
def self.from_json json
|
10
|
+
Collins::Ipmi.new json
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize opts = {}
|
14
|
+
hash = symbolize_hash(opts).inject({}) do |result, (k,v)|
|
15
|
+
key = k.to_s.downcase.sub(/^ipmi_/, "").to_sym
|
16
|
+
result[key] = v
|
17
|
+
result
|
18
|
+
end
|
19
|
+
@address = hash[:address].to_s
|
20
|
+
@asset_id = hash[:asset_id].to_s.to_i
|
21
|
+
@gateway = hash[:gateway].to_s
|
22
|
+
@id = hash[:id].to_s.to_i
|
23
|
+
@netmask = hash[:netmask].to_s
|
24
|
+
@password = hash[:password].to_s
|
25
|
+
@username = hash[:username].to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
def empty?
|
29
|
+
@id == 0
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
if empty? then
|
34
|
+
"Ipmi(None)"
|
35
|
+
else
|
36
|
+
"Ipmi(id = #{id}, asset_id = #{asset_id}, address = #{address}, gateway = #{gateway}, netmask = #{netmask}, username = #{username}, password = #{password})"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'collins/monkeypatch'
|
2
|
+
require 'collins/option'
|
3
|
+
|
4
|
+
module Collins; module Util
|
5
|
+
|
6
|
+
module Logging
|
7
|
+
|
8
|
+
DEFAULT_LOG_FORMAT = "%Y-%m-%d %H:%M:%S.%L"
|
9
|
+
|
10
|
+
def get_logger options = {}
|
11
|
+
return options[:logger] if options[:logger]
|
12
|
+
trace = Collins::Option(options[:trace]).get_or_else(false)
|
13
|
+
debug = Collins::Option(options[:debug]).get_or_else(false)
|
14
|
+
progname = Collins::Option(options[:progname] || options[:program]).get_or_else('unknown')
|
15
|
+
logfile = Collins::Option(options[:logfile]).get_or_else(STDOUT)
|
16
|
+
logger = Logger.new(logfile)
|
17
|
+
if trace then
|
18
|
+
logger.level = Logger::TRACE
|
19
|
+
elsif debug then
|
20
|
+
logger.level = Logger::DEBUG
|
21
|
+
else
|
22
|
+
logger.level = Logger::INFO
|
23
|
+
end
|
24
|
+
logger.progname = File.basename(progname)
|
25
|
+
logger.formatter = Proc.new do |severity, datetime, progname, message|
|
26
|
+
date_s = datetime.strftime(Collins::Util::Logging::DEFAULT_LOG_FORMAT)
|
27
|
+
"#{severity} [#{date_s}] #{progname}: #{message}\n"
|
28
|
+
end
|
29
|
+
logger
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end; end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
class Logger
|
4
|
+
|
5
|
+
module Severity
|
6
|
+
TRACE = -1
|
7
|
+
end
|
8
|
+
|
9
|
+
def trace?; @level <= TRACE; end
|
10
|
+
|
11
|
+
def trace(progname = nil, &block)
|
12
|
+
add(TRACE, nil, progname, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def format_severity severity
|
17
|
+
if severity == TRACE then
|
18
|
+
'TRACE'
|
19
|
+
else
|
20
|
+
SEV_LABEL[severity] || 'ANY'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,220 @@
|
|
1
|
+
module Collins
|
2
|
+
|
3
|
+
# Convenience method for creating an `Option`
|
4
|
+
def self.Option value
|
5
|
+
if value.nil? then
|
6
|
+
::Collins::None()
|
7
|
+
else
|
8
|
+
::Collins::Some(value)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
# Convenience method for creating a `None`
|
12
|
+
def self.None
|
13
|
+
::Collins::None.new
|
14
|
+
end
|
15
|
+
# Convenience method for creating a `Some`
|
16
|
+
def self.Some value
|
17
|
+
::Collins::Some.new(value)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Represents optional values. Instances of `Option` are either an instance of `Some` or `None`
|
21
|
+
# @note This is pretty much a straight rip off of the scala version
|
22
|
+
# @example
|
23
|
+
# name = get_parameter("name")
|
24
|
+
# upper = Option(name).map{|s| s.strip}.filter{|s|s.size > 0}.map{|s|s.upcase}
|
25
|
+
# puts(upper.get_or_else(""))
|
26
|
+
class Option
|
27
|
+
|
28
|
+
# @return [Boolean] True if the value is undefined
|
29
|
+
def empty?
|
30
|
+
raise NotImplementedError.new("empty? not implemented")
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Boolean] True if the value is defined
|
34
|
+
def defined?
|
35
|
+
!empty?
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Object] Value, if defined
|
39
|
+
# @raise [NameError] if value is undefined
|
40
|
+
def get
|
41
|
+
raise NotImplementedError.new("get not implemented")
|
42
|
+
end
|
43
|
+
|
44
|
+
# The value associated with this option, or the default
|
45
|
+
#
|
46
|
+
# @example
|
47
|
+
# # Raises an exception
|
48
|
+
# Option(nil).get_or_else { raise Exception.new("Stuff") }
|
49
|
+
# # Returns -1
|
50
|
+
# Option("23").map {|i| i.to_i}.filter{|i| i > 25}.get_or_else -1
|
51
|
+
#
|
52
|
+
# @param [Object] default A default value to use if the option value is undefined
|
53
|
+
# @yield [] Provide a default with a block instead of a parameter
|
54
|
+
# @return [Object] If None, default, otherwise the value
|
55
|
+
def get_or_else *default
|
56
|
+
if empty? then
|
57
|
+
if block_given? then
|
58
|
+
yield
|
59
|
+
else
|
60
|
+
default.first
|
61
|
+
end
|
62
|
+
else
|
63
|
+
get
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Return this `Option` if non-empty, otherwise return the result of evaluating the default
|
68
|
+
# @example
|
69
|
+
# Option(nil).or_else { "foo" } == Some("foo")
|
70
|
+
# @return [Option<Object>]
|
71
|
+
def or_else *default
|
72
|
+
if empty? then
|
73
|
+
res = if block_given? then
|
74
|
+
yield
|
75
|
+
else
|
76
|
+
default.first
|
77
|
+
end
|
78
|
+
if res.is_a?(Option) then
|
79
|
+
res
|
80
|
+
else
|
81
|
+
::Collins::Option(res)
|
82
|
+
end
|
83
|
+
else
|
84
|
+
self
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Return true if non-empty and predicate is true for the value
|
89
|
+
# @return [Boolean] test passed
|
90
|
+
def exists? &predicate
|
91
|
+
!empty? && predicate.call(get)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Apply the block specified to the value if non-empty
|
95
|
+
# @return [NilClass]
|
96
|
+
def foreach &f
|
97
|
+
if self.defined? then
|
98
|
+
f.call(get)
|
99
|
+
end
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
|
103
|
+
# If the option value is defined, apply the specified block to that value
|
104
|
+
#
|
105
|
+
# @example
|
106
|
+
# Option("15").map{|i| i.to_i}.get == 15
|
107
|
+
#
|
108
|
+
# @yieldparam [Object] block The current value
|
109
|
+
# @yieldreturn [Object] The new value
|
110
|
+
# @return [Option<Object>] Optional value
|
111
|
+
def map &block
|
112
|
+
if empty? then
|
113
|
+
None.new
|
114
|
+
else
|
115
|
+
Some.new(block.call(get))
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Same as map, but flatten the results
|
120
|
+
#
|
121
|
+
# This is useful when operating on an object that will return an `Option`.
|
122
|
+
#
|
123
|
+
# @example
|
124
|
+
# Option(15).flat_map {|i| Option(i).filter{|i2| i2 > 0}} == Some(15)
|
125
|
+
#
|
126
|
+
# @see #map
|
127
|
+
# @return [Option<Object>] Optional value
|
128
|
+
def flat_map &block
|
129
|
+
if empty? then
|
130
|
+
None.new
|
131
|
+
else
|
132
|
+
res = block.call(get)
|
133
|
+
if res.is_a?(Some) then
|
134
|
+
res
|
135
|
+
else
|
136
|
+
Some.new(res)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Convert to `None` if predicate fails
|
142
|
+
#
|
143
|
+
# Returns this option if it is non-empty *and* applying the predicate to this options returns
|
144
|
+
# true. Otherwise return `None`.
|
145
|
+
#
|
146
|
+
# @yieldparam [Object] predicate The current value
|
147
|
+
# @yieldreturn [Boolean] result of testing value
|
148
|
+
# @return [Option<Object>] `None` if predicate fails, or already `None`
|
149
|
+
def filter &predicate
|
150
|
+
if empty? || predicate.call(get) then
|
151
|
+
self
|
152
|
+
else
|
153
|
+
None.new
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Inverse of `filter` operation.
|
158
|
+
#
|
159
|
+
# Returns this option if it is non-empty *and* applying the predicate to this option returns
|
160
|
+
# false. Otherwise return `None`.
|
161
|
+
#
|
162
|
+
# @see #filter
|
163
|
+
# @return [Option<Object>]
|
164
|
+
def filter_not &predicate
|
165
|
+
if empty? || !predicate.call(get) then
|
166
|
+
self
|
167
|
+
else
|
168
|
+
None.new
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Represents a missing value
|
174
|
+
class None < Option
|
175
|
+
# Always true for `None`
|
176
|
+
# @see Option#empty?
|
177
|
+
def empty?
|
178
|
+
true
|
179
|
+
end
|
180
|
+
# Always raises a NameError
|
181
|
+
# @raise [NameError]
|
182
|
+
def get
|
183
|
+
raise NameError.new("None.get")
|
184
|
+
end
|
185
|
+
def eql? other
|
186
|
+
self.class.equal?(other.class)
|
187
|
+
end
|
188
|
+
alias == eql?
|
189
|
+
end
|
190
|
+
|
191
|
+
# Represents a present value
|
192
|
+
#
|
193
|
+
# A number of equality and comparison methods are implemented so that `Some` values are compared
|
194
|
+
# using the value of `x`.
|
195
|
+
class Some < Option
|
196
|
+
def initialize value
|
197
|
+
@x = value
|
198
|
+
end
|
199
|
+
def empty?
|
200
|
+
false
|
201
|
+
end
|
202
|
+
def get
|
203
|
+
x
|
204
|
+
end
|
205
|
+
def eql? other
|
206
|
+
self.class.equal?(other.class) && x.eql?(other.x)
|
207
|
+
end
|
208
|
+
alias == eql?
|
209
|
+
def hash
|
210
|
+
x.hash
|
211
|
+
end
|
212
|
+
def <=>(other)
|
213
|
+
self.class == other.class ?
|
214
|
+
(x <=> other.x) : nil
|
215
|
+
end
|
216
|
+
protected
|
217
|
+
attr_reader :x
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'collins/errors'
|
2
|
+
|
3
|
+
module Collins
|
4
|
+
|
5
|
+
class InvalidPowerStatus < CollinsError; end
|
6
|
+
|
7
|
+
class PowerUnit
|
8
|
+
include Collins::Util
|
9
|
+
|
10
|
+
attr_accessor :key, :value, :type, :label, :position, :is_required, :unique
|
11
|
+
|
12
|
+
def initialize model = {}
|
13
|
+
hash = symbolize_hash(model).inject({}) do |result, (k,v)|
|
14
|
+
result[k.downcase] = v
|
15
|
+
result
|
16
|
+
end
|
17
|
+
@key = hash[:key].to_s
|
18
|
+
@value = hash[:value].to_s
|
19
|
+
@type = hash[:type].to_s
|
20
|
+
@label = hash[:label].to_s
|
21
|
+
@position = hash[:position].to_s.to_i
|
22
|
+
@is_required = hash[:is_required]
|
23
|
+
@unique = hash[:unique]
|
24
|
+
end
|
25
|
+
|
26
|
+
def is_required?
|
27
|
+
@is_required == true
|
28
|
+
end
|
29
|
+
def unique?
|
30
|
+
@unique == true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Power
|
35
|
+
|
36
|
+
include Collins::Util
|
37
|
+
|
38
|
+
attr_accessor :unit_id, :units
|
39
|
+
|
40
|
+
class << self
|
41
|
+
def from_json json
|
42
|
+
return [] if (json.nil? or json.empty?)
|
43
|
+
if not json.is_a?(Array) then
|
44
|
+
json = [json]
|
45
|
+
end
|
46
|
+
json.map { |j| Collins::Power.new j }
|
47
|
+
end
|
48
|
+
def normalize_action action
|
49
|
+
case action.to_s.downcase.to_sym
|
50
|
+
when :off, :poweroff
|
51
|
+
"powerOff"
|
52
|
+
when :on, :poweron
|
53
|
+
"powerOn"
|
54
|
+
when :powersoft
|
55
|
+
"powerSoft"
|
56
|
+
when :soft, :rebootsoft
|
57
|
+
"rebootSoft"
|
58
|
+
when :hard, :reboothard
|
59
|
+
"rebootHard"
|
60
|
+
when :status, :powerstate
|
61
|
+
"powerState"
|
62
|
+
when :verify
|
63
|
+
"verify"
|
64
|
+
when :identify
|
65
|
+
"identify"
|
66
|
+
else
|
67
|
+
raise InvalidPowerStatus.new("#{action} is not a valid power status")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def initialize model = {}
|
73
|
+
hash = symbolize_hash(model).inject({}) do |result, (k,v)|
|
74
|
+
result[k.downcase] = v
|
75
|
+
result
|
76
|
+
end
|
77
|
+
@unit_id = hash[:unit_id].to_s.to_i
|
78
|
+
@units = (hash[:units] || []).map {|u| Collins::PowerUnit.new(u)}
|
79
|
+
end
|
80
|
+
|
81
|
+
def keys
|
82
|
+
units.map{|u| u.key }
|
83
|
+
end
|
84
|
+
def values
|
85
|
+
units.map{|u| u.value}
|
86
|
+
end
|
87
|
+
def types
|
88
|
+
units.map{|u| u.type}
|
89
|
+
end
|
90
|
+
def labels
|
91
|
+
units.map{|u| u.label}
|
92
|
+
end
|
93
|
+
def positions
|
94
|
+
units.map{|u| u.position}
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|