idrac 0.1.31 → 0.1.38

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.
@@ -1,366 +0,0 @@
1
- require 'tempfile'
2
- require 'net/http'
3
- require 'uri'
4
- require 'json'
5
- require 'nokogiri'
6
- require 'fileutils'
7
- require 'securerandom'
8
-
9
- module IDRAC
10
- class Firmware
11
- attr_reader :client
12
-
13
- CATALOG_URL = "https://downloads.dell.com/catalog/Catalog.xml.gz"
14
-
15
- def initialize(client)
16
- @client = client
17
- end
18
-
19
- def update(firmware_path, options = {})
20
- # Validate firmware file exists
21
- unless File.exist?(firmware_path)
22
- raise Error, "Firmware file not found: #{firmware_path}"
23
- end
24
-
25
- # Login to iDRAC
26
- client.login unless client.instance_variable_get(:@session_id)
27
-
28
- # Upload firmware file
29
- job_id = upload_firmware(firmware_path)
30
-
31
- # Check if we should wait for the update to complete
32
- if options[:wait]
33
- wait_for_job_completion(job_id, options[:timeout] || 3600)
34
- end
35
-
36
- job_id
37
- end
38
-
39
- def download_catalog(output_dir = nil)
40
- output_dir ||= Dir.pwd
41
- FileUtils.mkdir_p(output_dir) unless Dir.exist?(output_dir)
42
-
43
- catalog_gz_path = File.join(output_dir, "Catalog.xml.gz")
44
- catalog_path = File.join(output_dir, "Catalog.xml")
45
-
46
- puts "Downloading Dell catalog from #{CATALOG_URL}..."
47
-
48
- uri = URI.parse(CATALOG_URL)
49
- Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
50
- request = Net::HTTP::Get.new(uri)
51
- http.request(request) do |response|
52
- if response.code == "200"
53
- File.open(catalog_gz_path, 'wb') do |file|
54
- response.read_body do |chunk|
55
- file.write(chunk)
56
- end
57
- end
58
- else
59
- raise Error, "Failed to download catalog: #{response.code} #{response.message}"
60
- end
61
- end
62
- end
63
-
64
- puts "Extracting catalog..."
65
- system("gunzip -f #{catalog_gz_path}")
66
-
67
- if File.exist?(catalog_path)
68
- puts "Catalog downloaded and extracted to #{catalog_path}"
69
- return catalog_path
70
- else
71
- raise Error, "Failed to extract catalog"
72
- end
73
- end
74
-
75
- def get_system_inventory
76
- puts "Retrieving system inventory..."
77
-
78
- # Get basic system information
79
- system_uri = URI.parse("#{client.base_url}/redfish/v1/Systems/System.Embedded.1")
80
- system_response = client.authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1")
81
-
82
- if system_response.status != 200
83
- raise Error, "Failed to get system information: #{system_response.status}"
84
- end
85
-
86
- system_data = JSON.parse(system_response.body)
87
-
88
- # Get firmware inventory
89
- firmware_uri = URI.parse("#{client.base_url}/redfish/v1/UpdateService/FirmwareInventory")
90
- firmware_response = client.authenticated_request(:get, "/redfish/v1/UpdateService/FirmwareInventory")
91
-
92
- if firmware_response.status != 200
93
- raise Error, "Failed to get firmware inventory: #{firmware_response.status}"
94
- end
95
-
96
- firmware_data = JSON.parse(firmware_response.body)
97
-
98
- # Get detailed firmware information for each component
99
- firmware_inventory = []
100
-
101
- if firmware_data['Members'] && firmware_data['Members'].is_a?(Array)
102
- firmware_data['Members'].each do |member|
103
- if member['@odata.id']
104
- component_uri = member['@odata.id']
105
- component_response = client.authenticated_request(:get, component_uri)
106
-
107
- if component_response.status == 200
108
- component_data = JSON.parse(component_response.body)
109
- firmware_inventory << {
110
- name: component_data['Name'],
111
- id: component_data['Id'],
112
- version: component_data['Version'],
113
- updateable: component_data['Updateable'] || false,
114
- status: component_data['Status'] ? component_data['Status']['State'] : 'Unknown'
115
- }
116
- end
117
- end
118
- end
119
- end
120
-
121
- {
122
- system: {
123
- model: system_data['Model'],
124
- manufacturer: system_data['Manufacturer'],
125
- serial_number: system_data['SerialNumber'],
126
- part_number: system_data['PartNumber'],
127
- bios_version: system_data['BiosVersion'],
128
- service_tag: system_data['SKU']
129
- },
130
- firmware: firmware_inventory
131
- }
132
- end
133
-
134
- def check_updates(catalog_path = nil)
135
- # Download catalog if not provided
136
- catalog_path ||= download_catalog
137
-
138
- # Get system inventory
139
- inventory = get_system_inventory
140
-
141
- # Parse catalog
142
- catalog_doc = File.open(catalog_path) { |f| Nokogiri::XML(f) }
143
-
144
- # Extract service tag
145
- service_tag = inventory[:system][:service_tag]
146
-
147
- puts "Checking updates for system with service tag: #{service_tag}"
148
-
149
- # Find applicable updates
150
- updates = []
151
-
152
- # Get current firmware versions
153
- current_versions = {}
154
- inventory[:firmware].each do |fw|
155
- current_versions[fw[:name]] = fw[:version]
156
- end
157
-
158
- # Find matching components in catalog
159
- catalog_doc.xpath('//SoftwareComponent').each do |component|
160
- name = component.at_xpath('Name')&.text
161
- version = component.at_xpath('Version')&.text
162
- path = component.at_xpath('Path')&.text
163
- component_type = component.at_xpath('ComponentType')&.text
164
-
165
- # Check if this component matches any of our firmware
166
- inventory[:firmware].each do |fw|
167
- if fw[:name].include?(name) || name.include?(fw[:name])
168
- current_version = fw[:version]
169
-
170
- # Simple version comparison (this could be improved)
171
- if version != current_version
172
- updates << {
173
- name: name,
174
- current_version: current_version,
175
- available_version: version,
176
- path: path,
177
- component_type: component_type,
178
- download_url: "https://downloads.dell.com/#{path}"
179
- }
180
- end
181
- end
182
- end
183
- end
184
-
185
- updates
186
- end
187
-
188
- def interactive_update(catalog_path = nil)
189
- updates = check_updates(catalog_path)
190
-
191
- if updates.empty?
192
- puts "No updates available for your system."
193
- return
194
- end
195
-
196
- puts "\nAvailable updates:"
197
- updates.each_with_index do |update, index|
198
- puts "#{index + 1}. #{update[:name]}: #{update[:current_version]} -> #{update[:available_version]}"
199
- end
200
-
201
- puts "\nEnter the number of the update to install (or 'all' for all updates, 'q' to quit):"
202
- choice = STDIN.gets.chomp
203
-
204
- return if choice.downcase == 'q'
205
-
206
- selected_updates = if choice.downcase == 'all'
207
- updates
208
- else
209
- index = choice.to_i - 1
210
- if index >= 0 && index < updates.size
211
- [updates[index]]
212
- else
213
- puts "Invalid selection."
214
- return
215
- end
216
- end
217
-
218
- selected_updates.each do |update|
219
- puts "Downloading #{update[:name]} version #{update[:available_version]}..."
220
-
221
- # Create temp directory
222
- temp_dir = Dir.mktmpdir
223
-
224
- begin
225
- # Download the update
226
- update_filename = File.basename(update[:path])
227
- update_path = File.join(temp_dir, update_filename)
228
-
229
- uri = URI.parse(update[:download_url])
230
- Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
231
- request = Net::HTTP::Get.new(uri)
232
- http.request(request) do |response|
233
- if response.code == "200"
234
- File.open(update_path, 'wb') do |file|
235
- response.read_body do |chunk|
236
- file.write(chunk)
237
- end
238
- end
239
- else
240
- puts "Failed to download update: #{response.code} #{response.message}"
241
- next
242
- end
243
- end
244
- end
245
-
246
- puts "Installing #{update[:name]} version #{update[:available_version]}..."
247
- job_id = update(update_path, wait: true)
248
- puts "Update completed with job ID: #{job_id}"
249
-
250
- ensure
251
- # Clean up temp directory
252
- FileUtils.remove_entry(temp_dir)
253
- end
254
- end
255
- end
256
-
257
- private
258
-
259
- def upload_firmware(firmware_path)
260
- puts "Uploading firmware file: #{firmware_path}"
261
-
262
- # Get the HttpPushUri from UpdateService
263
- update_service_response = client.authenticated_request(:get, "/redfish/v1/UpdateService")
264
- update_service_data = JSON.parse(update_service_response.body)
265
-
266
- http_push_uri = update_service_data['HttpPushUri']
267
- if http_push_uri.nil?
268
- http_push_uri = "/redfish/v1/UpdateService/FirmwareInventory"
269
- puts "HttpPushUri not found, using default: #{http_push_uri}"
270
- else
271
- puts "Found HttpPushUri: #{http_push_uri}"
272
- end
273
-
274
- # Get the ETag for the HttpPushUri
275
- etag_response = client.authenticated_request(:get, http_push_uri)
276
- etag = etag_response.headers['etag']
277
-
278
- puts "Got ETag: #{etag}"
279
-
280
- # Create a boundary for multipart/form-data
281
- boundary = "----WebKitFormBoundary#{SecureRandom.hex(16)}"
282
-
283
- # Read the file content
284
- file_content = File.binread(firmware_path)
285
- filename = File.basename(firmware_path)
286
-
287
- # Create the multipart body
288
- post_body = []
289
- post_body << "--#{boundary}\r\n"
290
- post_body << "Content-Disposition: form-data; name=\"file\"; filename=\"#{filename}\"\r\n"
291
- post_body << "Content-Type: application/octet-stream\r\n\r\n"
292
- post_body << file_content
293
- post_body << "\r\n--#{boundary}--\r\n"
294
-
295
- # Upload the firmware
296
- response = client.authenticated_request(
297
- :post,
298
- http_push_uri,
299
- {
300
- headers: {
301
- 'Content-Type' => "multipart/form-data; boundary=#{boundary}",
302
- 'If-Match' => etag
303
- },
304
- body: post_body.join
305
- }
306
- )
307
-
308
- if response.status < 200 || response.status >= 300
309
- raise Error, "Firmware upload failed with status #{response.status}: #{response.body}"
310
- end
311
-
312
- # Extract job ID from response
313
- response_data = JSON.parse(response.body)
314
- job_id = response_data['Id'] || response_data['TaskId']
315
-
316
- if job_id.nil?
317
- raise Error, "Failed to extract job ID from firmware upload response"
318
- end
319
-
320
- puts "Firmware update job created with ID: #{job_id}"
321
- job_id
322
- end
323
-
324
- def wait_for_job_completion(job_id, timeout)
325
- puts "Waiting for firmware update job #{job_id} to complete..."
326
-
327
- start_time = Time.now
328
- loop do
329
- status = get_job_status(job_id)
330
-
331
- case status
332
- when 'Completed'
333
- puts "Firmware update completed successfully"
334
- return true
335
- when 'Failed'
336
- raise Error, "Firmware update job failed"
337
- when 'Scheduled', 'Running', 'Downloading', 'Pending'
338
- # Job still in progress
339
- else
340
- puts "Unknown job status: #{status}"
341
- end
342
-
343
- if Time.now - start_time > timeout
344
- raise Error, "Firmware update timed out after #{timeout} seconds"
345
- end
346
-
347
- # Wait before checking again
348
- sleep 10
349
- end
350
- end
351
-
352
- def get_job_status(job_id)
353
- response = client.authenticated_request(
354
- :get,
355
- "/redfish/v1/TaskService/Tasks/#{job_id}"
356
- )
357
-
358
- if response.status != 200
359
- raise Error, "Failed to get job status with status #{response.status}: #{response.body}"
360
- end
361
-
362
- response_data = JSON.parse(response.body)
363
- response_data['TaskState'] || 'Unknown'
364
- end
365
- end
366
- end
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module IDRAC
4
- VERSION = "0.1.6"
5
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'httparty'
4
- require 'nokogiri'
5
- require 'faraday'
6
- require 'faraday/multipart'
7
- require 'base64'
8
- require 'uri'
9
- # If dev, required debug
10
- require 'debug' if ENV['RUBY_ENV'] == 'development'
11
-
12
- require_relative "idrac/version"
13
- require_relative "idrac/client"
14
- require_relative "idrac/screenshot"
15
- require_relative "idrac/firmware"
16
-
17
- module IDRAC
18
- class Error < StandardError; end
19
-
20
- def self.new(host:, username:, password:, port: 443, use_ssl: true, verify_ssl: true)
21
- Client.new(
22
- host: host,
23
- username: username,
24
- password: password,
25
- port: port,
26
- use_ssl: use_ssl,
27
- verify_ssl: verify_ssl
28
- )
29
- end
30
- end
@@ -1,4 +0,0 @@
1
- module Idrac
2
- VERSION: String
3
- # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
- end
data/idrac-0.1.7/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
@@ -1,103 +0,0 @@
1
- # IDRAC
2
-
3
- A Ruby client for the Dell iDRAC API. This gem provides a command-line interface and a Ruby API for interacting with Dell iDRAC servers.
4
-
5
- ## Features
6
-
7
- - Take screenshots of the iDRAC console
8
- - Update firmware using Dell's catalog
9
- - Check for firmware updates
10
- - Interactive firmware update process
11
-
12
- ## Installation
13
-
14
- Add this line to your application's Gemfile:
15
-
16
- ```ruby
17
- gem 'idrac'
18
- ```
19
-
20
- And then execute:
21
-
22
- $ bundle install
23
-
24
- Or install it yourself as:
25
-
26
- $ gem install idrac
27
-
28
- ## Usage
29
-
30
- ### Command Line Interface
31
-
32
- The gem provides a command-line interface for interacting with iDRAC servers:
33
-
34
- ```bash
35
- # Take a screenshot of the iDRAC console
36
- idrac screenshot --host=192.168.1.100 --username=root --password=calvin
37
- # Specify a custom output filename
38
- idrac screenshot --host=192.168.1.100 --username=root --password=calvin --output=my_screenshot.png
39
-
40
- # Download the Dell firmware catalog
41
- idrac firmware:catalog --host=192.168.1.100 --username=root --password=calvin
42
-
43
- # Check firmware status and available updates
44
- idrac firmware:status --host=192.168.1.100 --username=root --password=calvin
45
-
46
- # Update firmware using a specific file
47
- idrac firmware:update /path/to/firmware.exe --host=192.168.1.100 --username=root --password=calvin
48
-
49
- # Interactive firmware update
50
- idrac firmware:interactive --host=192.168.1.100 --username=root --password=calvin
51
- ```
52
-
53
- ### Ruby API
54
-
55
- ```ruby
56
- require 'idrac'
57
-
58
- # Create a client
59
- client = IDRAC.new(
60
- host: '192.168.1.100',
61
- username: 'root',
62
- password: 'calvin'
63
- )
64
-
65
- # Take a screenshot (using the client convenience method)
66
- filename = client.screenshot
67
- puts "Screenshot saved to: #{filename}"
68
-
69
- # Or use the Screenshot class directly for more control
70
- screenshot = IDRAC::Screenshot.new(client)
71
- filename = screenshot.capture
72
- puts "Screenshot saved to: #{filename}"
73
-
74
- # Firmware operations
75
- firmware = IDRAC::Firmware.new(client)
76
-
77
- # Download catalog
78
- catalog_path = firmware.download_catalog
79
-
80
- # Get system inventory
81
- inventory = firmware.get_system_inventory
82
- puts "Service Tag: #{inventory[:system][:service_tag]}"
83
-
84
- # Check for updates
85
- updates = firmware.check_updates(catalog_path)
86
- updates.each do |update|
87
- puts "#{update[:name]}: #{update[:current_version]} -> #{update[:available_version]}"
88
- end
89
-
90
- # Update firmware
91
- job_id = firmware.update('/path/to/firmware.exe', wait: true)
92
- puts "Update completed with job ID: #{job_id}"
93
- ```
94
-
95
- ## Development
96
-
97
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
98
-
99
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
100
-
101
- ## Contributing
102
-
103
- Bug reports and pull requests are welcome on GitHub at https://github.com/usiegj00/idrac.
data/idrac-0.1.7/Rakefile DELETED
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- task default: :spec
9
-
10
- # Add a task that tags and pushes to the repository and builds
11
- # the gem and pushes it to rubygems.org.
12
- # Depend on the build task to ensure the gem is up to date.
13
- task :release => [:build] do
14
- system "git tag v#{Idrac::VERSION}"
15
- system "git push --tags"
16
- system "gem push pkg/idrac-#{Idrac::VERSION}.gem"
17
- end
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require "bundler/setup"
5
- require "idrac"
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- require "irb"
11
- IRB.start(__FILE__)