radfish 0.2.8 → 0.2.9
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.
- checksums.yaml +4 -4
- data/lib/radfish/client.rb +37 -1
- data/lib/radfish/http_client.rb +73 -13
- data/lib/radfish/vendor_detector.rb +13 -3
- data/lib/radfish/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3eb7e0fad9f1c58627bb67908c36b48ee6345502dd697d060bfb2a2269168779
|
4
|
+
data.tar.gz: 1d0773d739fc075885f9c49abaf719394695af6e7120c2d42b0478638f78d0f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 86cb61d2ce44dc0443bd41236074670645e8f16b8d6daa316b54159a6ebcacdf60184cc29ccbbce10747cd878a38b596703b631b2ef8b7e7fa8f922718928ed5
|
7
|
+
data.tar.gz: ee102d72d959c36cf16f997ebf8bacb9ae8ed6811b68fc6a08961510f85565e46ec132f42bd6d58da5b06af0acfb12fa543b9e3178abc1e86b69bfde01a582b9
|
data/lib/radfish/client.rb
CHANGED
@@ -18,7 +18,8 @@ module Radfish
|
|
18
18
|
password: password,
|
19
19
|
port: options[:port] || 443,
|
20
20
|
use_ssl: options.fetch(:use_ssl, true),
|
21
|
-
verify_ssl: options.fetch(:verify_ssl, false)
|
21
|
+
verify_ssl: options.fetch(:verify_ssl, false),
|
22
|
+
host_header: options[:host_header]
|
22
23
|
)
|
23
24
|
detector.verbosity = @verbosity
|
24
25
|
@vendor = detector.detect
|
@@ -186,6 +187,41 @@ module Radfish
|
|
186
187
|
Array(raw).map { |v| build_volume(v, controller) }
|
187
188
|
end
|
188
189
|
|
190
|
+
# Storage management methods - pass through to adapter
|
191
|
+
# These are Dell-specific for now but will be generalized when other vendors add support
|
192
|
+
|
193
|
+
def enable_local_key_management(controller_id:, passphrase:, key_id:)
|
194
|
+
if @adapter.respond_to?(:enable_local_key_management)
|
195
|
+
@adapter.enable_local_key_management(controller_id: controller_id, passphrase: passphrase, key_id: key_id)
|
196
|
+
else
|
197
|
+
raise NotImplementedError, "Local key management not supported by #{@vendor} adapter"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def disable_local_key_management(controller_id:)
|
202
|
+
if @adapter.respond_to?(:disable_local_key_management)
|
203
|
+
@adapter.disable_local_key_management(controller_id: controller_id)
|
204
|
+
else
|
205
|
+
raise NotImplementedError, "Local key management not supported by #{@vendor} adapter"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def create_virtual_disk(**options)
|
210
|
+
if @adapter.respond_to?(:create_virtual_disk)
|
211
|
+
@adapter.create_virtual_disk(**options)
|
212
|
+
else
|
213
|
+
raise NotImplementedError, "Virtual disk creation not supported by #{@vendor} adapter"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def delete_volume(volume_odata_id)
|
218
|
+
if @adapter.respond_to?(:delete_volume)
|
219
|
+
@adapter.delete_volume(volume_odata_id)
|
220
|
+
else
|
221
|
+
raise NotImplementedError, "Volume deletion not supported by #{@vendor} adapter"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
189
225
|
private
|
190
226
|
|
191
227
|
def build_controller(raw)
|
data/lib/radfish/http_client.rb
CHANGED
@@ -4,6 +4,7 @@ require 'faraday'
|
|
4
4
|
require 'faraday/multipart'
|
5
5
|
require 'faraday/retry'
|
6
6
|
require 'logger'
|
7
|
+
require 'socket'
|
7
8
|
|
8
9
|
module Radfish
|
9
10
|
# Shared HTTP client for all BMC connections
|
@@ -54,13 +55,28 @@ module Radfish
|
|
54
55
|
end
|
55
56
|
|
56
57
|
def request(method, path, body: nil, headers: {}, auth: true, timeout: nil, **options)
|
57
|
-
|
58
|
+
debug "Starting HTTP #{method.upcase} request to #{path}", 2, :yellow
|
59
|
+
|
60
|
+
# Add host header if specified (needed for SSH tunnels to iDRAC)
|
61
|
+
if @options[:host_header]
|
62
|
+
headers = headers.merge('Host' => @options[:host_header])
|
63
|
+
debug "Added Host header: #{@options[:host_header]}", 2, :cyan
|
64
|
+
end
|
65
|
+
|
66
|
+
debug "Creating connection...", 2, :yellow
|
67
|
+
conn = connection(auth: auth)
|
68
|
+
debug "Connection created, sending #{method} request...", 2, :yellow
|
69
|
+
|
70
|
+
response = conn.send(method) do |req|
|
71
|
+
debug "Setting request URL: #{path}", 3, :cyan
|
58
72
|
req.url path
|
73
|
+
debug "Merging headers: #{headers}", 3, :cyan
|
59
74
|
req.headers.merge!(headers)
|
60
75
|
req.body = body if body
|
61
76
|
|
62
77
|
# Override timeout if specified
|
63
78
|
if timeout
|
79
|
+
debug "Setting timeout: #{timeout}s", 3, :cyan
|
64
80
|
req.options.timeout = timeout
|
65
81
|
req.options.open_timeout = [timeout / 2, 5].min
|
66
82
|
end
|
@@ -69,21 +85,46 @@ module Radfish
|
|
69
85
|
options.each do |key, value|
|
70
86
|
req.options[key] = value if req.options.respond_to?(:"#{key}=")
|
71
87
|
end
|
88
|
+
debug "Request configured, about to send...", 2, :yellow
|
72
89
|
end
|
73
90
|
|
91
|
+
debug "Request completed with status: #{response.status}", 2, :green
|
92
|
+
|
74
93
|
response
|
75
94
|
rescue Faraday::ConnectionFailed => e
|
76
95
|
debug "Connection failed: #{e.message}", 1, :red
|
77
|
-
raise ConnectionError, "Failed to connect to #{host}: #{e.message}"
|
96
|
+
raise Radfish::ConnectionError, "Failed to connect to #{host}: #{e.message}"
|
78
97
|
rescue Faraday::TimeoutError => e
|
79
98
|
debug "Request timed out: #{e.message}", 1, :red
|
80
|
-
raise TimeoutError, "Request to #{host} timed out: #{e.message}"
|
99
|
+
raise Radfish::TimeoutError, "Request to #{host} timed out: #{e.message}"
|
81
100
|
rescue Faraday::SSLError => e
|
82
101
|
debug "SSL error: #{e.message}", 1, :red
|
83
|
-
|
102
|
+
|
103
|
+
# Test if this might be HTTP instead of HTTPS
|
104
|
+
begin
|
105
|
+
debug "Testing if endpoint supports HTTP instead of HTTPS...", 2, :yellow
|
106
|
+
test_http_socket = TCPSocket.new(host, port)
|
107
|
+
test_http_socket.write("GET /redfish/v1 HTTP/1.1\r\nHost: #{@host_header || host}\r\nConnection: close\r\n\r\n")
|
108
|
+
response = test_http_socket.read(1024) # Read first 1KB
|
109
|
+
test_http_socket.close
|
110
|
+
|
111
|
+
if response && response.include?("HTTP/") && !response.include?("301") && !response.include?("302")
|
112
|
+
debug "HTTP response received - this endpoint might support HTTP instead of HTTPS", 1, :yellow
|
113
|
+
debug "HTTP response preview: #{response[0..200]}", 2
|
114
|
+
else
|
115
|
+
debug "No valid HTTP response - endpoint requires HTTPS but SSL failed", 2, :red
|
116
|
+
end
|
117
|
+
rescue => http_test_error
|
118
|
+
debug "HTTP test failed: #{http_test_error.message}", 2
|
119
|
+
end
|
120
|
+
|
121
|
+
raise Radfish::ConnectionError, "SSL error connecting to #{host}: #{e.message}"
|
84
122
|
rescue => e
|
85
123
|
debug "HTTP request failed: #{e.class} - #{e.message}", 1, :red
|
86
|
-
|
124
|
+
debug "Exception backtrace: #{e.backtrace.first(5).join("\n")}", 1, :red
|
125
|
+
|
126
|
+
# Don't re-raise as generic error - let the original exception propagate
|
127
|
+
raise e
|
87
128
|
end
|
88
129
|
|
89
130
|
private
|
@@ -92,7 +133,30 @@ module Radfish
|
|
92
133
|
@connections ||= {}
|
93
134
|
cache_key = auth ? :with_auth : :without_auth
|
94
135
|
|
95
|
-
|
136
|
+
ssl_options = {
|
137
|
+
verify: verify_ssl,
|
138
|
+
verify_mode: verify_ssl ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE,
|
139
|
+
# Try TLS 1.0 for older BMCs, fallback to 1.2
|
140
|
+
min_version: OpenSSL::SSL::TLS1_VERSION,
|
141
|
+
max_version: OpenSSL::SSL::TLS1_2_VERSION,
|
142
|
+
ciphers: 'ALL:!aNULL:!eNULL:!SSLv2' # More permissive for older BMCs
|
143
|
+
}
|
144
|
+
debug "SSL options: #{ssl_options.inspect}", 2, :yellow
|
145
|
+
|
146
|
+
# Test TCP connectivity first (for SSH tunnels)
|
147
|
+
if host.include?('localhost') && port
|
148
|
+
begin
|
149
|
+
debug "Testing TCP connectivity to #{host}:#{port}...", 2, :yellow
|
150
|
+
socket = TCPSocket.new(host, port)
|
151
|
+
socket.close
|
152
|
+
debug "TCP connection test successful", 2, :green
|
153
|
+
rescue => e
|
154
|
+
debug "TCP connection test failed: #{e.message}", 1, :red
|
155
|
+
raise Radfish::ConnectionError, "TCP connection test failed to #{host}:#{port}: #{e.message}"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
@connections[cache_key] ||= Faraday.new(url: base_url, ssl: ssl_options) do |faraday|
|
96
160
|
# Add authentication if credentials provided and auth is enabled
|
97
161
|
if auth && username && password
|
98
162
|
faraday.request :authorization, :basic, username, password
|
@@ -119,12 +183,8 @@ module Radfish
|
|
119
183
|
Faraday::RetriableResponse
|
120
184
|
],
|
121
185
|
methods: [:get, :put, :delete, :post, :patch],
|
122
|
-
retry_statuses: [408, 429, 500, 502, 503, 504]
|
123
|
-
retry_block
|
124
|
-
if verbosity > 0
|
125
|
-
debug "Retry #{retries}/#{options[:max]}: #{exception&.message || "HTTP #{env.status}"}", 1, :yellow
|
126
|
-
end
|
127
|
-
}
|
186
|
+
retry_statuses: [408, 429, 500, 502, 503, 504]
|
187
|
+
# Removed retry_block to debug ArgumentError - can add back later
|
128
188
|
}
|
129
189
|
|
130
190
|
# Set timeouts
|
@@ -133,7 +193,7 @@ module Radfish
|
|
133
193
|
|
134
194
|
# Add logging if verbose
|
135
195
|
if verbosity > 0
|
136
|
-
faraday.response :logger, Logger.new(STDOUT), bodies: verbosity >= 2 do |logger|
|
196
|
+
faraday.response :logger, Logger.new(STDOUT), { bodies: verbosity >= 2 } do |logger|
|
137
197
|
logger.filter(/(Authorization: Basic )([^,\n]+)/, '\1[FILTERED]')
|
138
198
|
logger.filter(/(Password"=>"?)([^,"]+)/, '\1[FILTERED]')
|
139
199
|
logger.filter(/("password":\s*")([^"]+)/, '\1[FILTERED]')
|
@@ -9,13 +9,14 @@ module Radfish
|
|
9
9
|
attr_reader :host, :username, :password, :port, :use_ssl, :verify_ssl
|
10
10
|
attr_accessor :verbosity
|
11
11
|
|
12
|
-
def initialize(host:, username:, password:, port: 443, use_ssl: true, verify_ssl: false)
|
12
|
+
def initialize(host:, username:, password:, port: 443, use_ssl: true, verify_ssl: false, host_header: nil)
|
13
13
|
@host = host
|
14
14
|
@username = username
|
15
15
|
@password = password
|
16
16
|
@port = port
|
17
17
|
@use_ssl = use_ssl
|
18
18
|
@verify_ssl = verify_ssl
|
19
|
+
@host_header = host_header
|
19
20
|
@verbosity = 0
|
20
21
|
|
21
22
|
# Use the shared HTTP client
|
@@ -26,6 +27,7 @@ module Radfish
|
|
26
27
|
verify_ssl: verify_ssl,
|
27
28
|
username: username,
|
28
29
|
password: password,
|
30
|
+
host_header: host_header, # Pass host_header to HttpClient
|
29
31
|
verbosity: 0, # Will be updated via verbosity= setter
|
30
32
|
retry_count: 2, # Fewer retries for detection
|
31
33
|
retry_delay: 0.5
|
@@ -39,8 +41,10 @@ module Radfish
|
|
39
41
|
|
40
42
|
def detect
|
41
43
|
debug "Detecting vendor for #{host}:#{port}...", 1, :cyan
|
44
|
+
debug "Host header: #{@host_header}" if @host_header
|
42
45
|
|
43
46
|
# Try to get the Redfish service root
|
47
|
+
debug "Fetching service root...", 2, :yellow
|
44
48
|
service_root = fetch_service_root
|
45
49
|
|
46
50
|
unless service_root
|
@@ -48,6 +52,7 @@ module Radfish
|
|
48
52
|
return nil
|
49
53
|
end
|
50
54
|
|
55
|
+
debug "Service root fetched successfully", 2, :green
|
51
56
|
vendor = identify_vendor(service_root)
|
52
57
|
debug "Detected vendor: #{vendor || 'Unknown'} for #{host}:#{port}", 1, vendor ? :green : :yellow
|
53
58
|
|
@@ -58,10 +63,15 @@ module Radfish
|
|
58
63
|
|
59
64
|
def fetch_service_root
|
60
65
|
begin
|
61
|
-
|
62
|
-
|
66
|
+
debug "About to make HTTP GET request to /redfish/v1", 2, :yellow
|
67
|
+
# Use longer timeout for SSH tunnels (15 seconds), shorter for direct connections (5 seconds)
|
68
|
+
timeout = @host_header ? 15 : 5
|
69
|
+
debug "Using timeout: #{timeout}s (SSH tunnel detected)" if @host_header
|
70
|
+
response = @http_client.get('/redfish/v1', timeout: timeout)
|
71
|
+
debug "HTTP GET request completed", 2, :green
|
63
72
|
|
64
73
|
if response.status == 200
|
74
|
+
debug "Got 200 response, parsing JSON...", 2, :green
|
65
75
|
JSON.parse(response.body)
|
66
76
|
elsif response.status == 401
|
67
77
|
debug "Authentication failed (HTTP 401) - check username/password", 1, :red
|
data/lib/radfish/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: radfish
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Siegel
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-09-
|
11
|
+
date: 2025-09-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|