rightimage_tools 0.2.1 → 0.5.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/bin/image_mover CHANGED
@@ -50,11 +50,11 @@ grant = RightAws::S3::Grantee.new(key_to, 'http://acs.amazonaws.com/groups/globa
50
50
  raise "Grant update failed" unless grant
51
51
 
52
52
  puts "Reindexing source bucket #{bucket_from}"
53
- indexer_from = RightImage::S3HtmlIndexer.new(options[:bucket_from], ENV["AWS_ACCESS_KEY_ID"], ENV["AWS_SECRET_ACCESS_KEY"])
53
+ indexer_from = RightImageTools::S3HtmlIndexer.new(options[:bucket_from], ENV["AWS_ACCESS_KEY_ID"], ENV["AWS_SECRET_ACCESS_KEY"])
54
54
  indexer_from.to_html("index-from.html")
55
55
  indexer_from.upload_index("index-from.html")
56
56
 
57
57
  puts "Reindexing destination bucket #{bucket_to}"
58
- indexer_to = RightImage::S3HtmlIndexer.new(options[:bucket_to], ENV["AWS_ACCESS_KEY_ID"], ENV["AWS_SECRET_ACCESS_KEY"])
58
+ indexer_to = RightImageTools::S3HtmlIndexer.new(options[:bucket_to], ENV["AWS_ACCESS_KEY_ID"], ENV["AWS_SECRET_ACCESS_KEY"])
59
59
  indexer_to.to_html("index-to.html")
60
60
  indexer_to.upload_index("index-to.html")
data/bin/mci_merge CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'rubygems'
4
4
  require 'optparse'
5
- #require 'ruby-debug'
5
+ require 'ruby-debug'
6
6
  require 'rest_connection'
7
7
  require 'tmpdir'
8
8
 
@@ -57,16 +57,21 @@ sources = []
57
57
  dests = []
58
58
 
59
59
  mcis.each do |mci|
60
- if mci.name =~ /RightImage.*#{Regexp.escape(options[:src])}$/
60
+ if mci.name =~ /RightImage.*#{Regexp.escape(options[:src])}(_EBS)?$/
61
61
  sources << MultiCloudImageInternal.find(mci.rs_id.to_i)
62
62
  end
63
63
  end
64
64
 
65
+ if sources.length == 0
66
+ puts "Did not find any MCIs with suffix #{options[:src]} to copy"
67
+ exit 0
68
+ end
69
+
65
70
  puts "Found #{sources.length} MCIs to move: "
66
71
 
67
72
  sources.each_with_index do |mci, i|
68
73
  # print out source mci
69
- print i.to_s.ljust(2)+") "
74
+ print (i+1).to_s.ljust(2)+") "
70
75
  puts mci.name
71
76
  clouds = mci.multi_cloud_image_cloud_settings.map {|s| s.cloud}
72
77
  puts " CLOUDS: "+clouds.join(", ")
@@ -74,8 +79,9 @@ sources.each_with_index do |mci, i|
74
79
 
75
80
  # find a destination mci and print that if found
76
81
  # else note we'll make a new mci for the destination
77
- name_no_suffix = mci.name.sub(options[:src],"")
82
+ name_no_suffix = mci.name.sub(options[:src],"").sub("_EBS","")
78
83
  dest_mci_name = name_no_suffix + options[:dest]
84
+ dest_mci_name += "_EBS" if mci.name =~ /_EBS/ and dest_mci_name !~ /_EBS/
79
85
  found = mcis.find { |new_mci| new_mci.name == dest_mci_name }
80
86
  if found
81
87
  dest_mci = MultiCloudImageInternal.find(found.rs_id.to_i)
data/bin/mci_report ADDED
@@ -0,0 +1,232 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'yaml'
5
+ require 'irb'
6
+ require 'right_api_client'
7
+ require 'rest_connection'
8
+ require 'trollop'
9
+ require 'highline/import'
10
+ require 'json'
11
+
12
+ # The only option needed for this program is the ServerTemplate ID.
13
+ trollop_parser = Trollop::Parser.new do
14
+ opt :st_id, "Generate a report for this Server Template (id)", :type => :integer
15
+ opt :st_list_file, "Generate a list of MCIs in Server Templates given in the file", :type => :string
16
+ opt :output_type, "Specify the type of output. Supported formats: text, xml, json", :type => :string
17
+ end
18
+
19
+ class MciReport
20
+
21
+ # Establish a connection to Right_API_Client during the initialization
22
+ def initialize
23
+ @client = RightApi::Client.new(YAML.load_file(File.expand_path('~/.right_api_client/login.yml', __FILE__)))
24
+ end
25
+
26
+ # This function creates a report of all the Multi Cloud Images in a ServerTemplate specified by the ST ID in the input.
27
+ def generate_mci_report(options)
28
+ mcis = @client.server_templates(:id => options[:st_id]).show.multi_cloud_images.index
29
+
30
+ # Go through all the Multi Cloud Images in the given ServerTemplate and pull the information required for printing the report.
31
+ outer_hash_array = Array.new
32
+ mcis.each do |mci|
33
+ outer_hash = Hash.new
34
+ # Name, Description, Revision, and MCI ID are obtained directly from the MCI object.
35
+ mci_id = mci.href.split("/").last.to_i
36
+ outer_hash['title'] = "Name: " + mci.name + " | MCI ID: " + mci_id.to_s + " | Revision: " + mci.show.revision.to_s + "\nDescription: " + mci.show.description
37
+ inner_hash_array = Array.new
38
+ inner_hash = Hash.new
39
+
40
+ # Add all titles in the first array element.
41
+ inner_hash['detail1'] = "Instance Type"
42
+ inner_hash['detail2'] = "Image Name"
43
+ inner_hash['detail3'] = "Cloud"
44
+ inner_hash_array << inner_hash
45
+
46
+ # This block uses the rest_connection for obtaining information about ec2 images.
47
+ rest_mci = MultiCloudImage.find(mci_id)
48
+ rest_mci_settings = rest_mci.find_and_flatten_settings
49
+ rest_mci_settings.each do |rest_mci_setting|
50
+ mci_params = rest_mci_setting.params
51
+ inner_hash = Hash.new
52
+
53
+ # Grab only the information about the ec2 images.
54
+ if mci_params['aws_instance_type']
55
+ inner_hash['detail1'] = mci_params['aws_instance_type']
56
+ inner_hash['detail2'] = mci_params['image_name']
57
+ inner_hash['detail3'] = mci_params['cloud']
58
+ end
59
+ inner_hash_array << inner_hash if inner_hash['detail1'] && inner_hash['detail2'] && inner_hash['detail3']
60
+ end
61
+
62
+ # This block uses the regular right_api_client for obtaining information about non-ec2 images.
63
+ right_mci_settings = mci.settings.index
64
+ right_mci_settings.each do |right_mci_setting|
65
+ inner_hash = Hash.new
66
+
67
+ # If the instance_type api method is not available, don't call the method rather print N/A for instance type in the report.
68
+ if right_mci_setting.api_methods.include?("instance_type")
69
+ inner_hash['detail1'] = right_mci_setting.instance_type.show.name
70
+ else
71
+ inner_hash['detail1'] = "N/A"
72
+ end
73
+ inner_hash['detail2'] = right_mci_setting.image.show.name
74
+ inner_hash['detail3'] = right_mci_setting.cloud.show.name
75
+ inner_hash_array << inner_hash if inner_hash['detail1'] && inner_hash['detail2'] && inner_hash['detail3']
76
+ end
77
+ outer_hash['detail'] = inner_hash_array
78
+ outer_hash_array << outer_hash
79
+ end
80
+ self.print_report(options, outer_hash_array, "RightScale MCI Image Report")
81
+ end
82
+
83
+
84
+ # This function gets the name and id hash and prepares a list of ServerTemplate IDs.
85
+ def servertemplate_list(servertemplate_name_id_hash)
86
+ list = []
87
+ servertemplate_name_id_hash.each do |name, id|
88
+ list << @client.server_templates(:id => id).show
89
+ end
90
+ list
91
+ end
92
+
93
+ # This function creates a report of all ServerTemplates specified in the input file.
94
+ def generate_servertemplate_report(options, servertemplate_name_id_hash)
95
+ outer_hash_array = Array.new
96
+ st_list = servertemplate_list(servertemplate_name_id_hash)
97
+ st_list.each do |st|
98
+ outer_hash = Hash.new
99
+ mcis = st.multi_cloud_images.index
100
+ outer_hash['title'] = "Name: " + st.name + " | ID: " + st.object_id.to_s
101
+ inner_hash_array = Array.new
102
+ inner_hash = Hash.new
103
+ inner_hash['detail1'] = "Revision"
104
+ inner_hash['detail2'] = "Name"
105
+ inner_hash['detail3'] = "MCI ID"
106
+ inner_hash_array << inner_hash if inner_hash['detail1'] && inner_hash['detail2'] && inner_hash['detail3']
107
+
108
+ # Go through all the Multi Cloud Images in the given ServerTemplate and pull the information required for printing the report.
109
+ mcis.each do |mci|
110
+ inner_hash = Hash.new
111
+ mci_id = mci.href.split("/").last.to_i
112
+ inner_hash['detail1'] = mci.show.revision
113
+ inner_hash['detail2'] = mci.name
114
+ inner_hash['detail3'] = mci_id
115
+ inner_hash_array << inner_hash if inner_hash['detail1'] && inner_hash['detail2'] && inner_hash['detail3']
116
+ end
117
+ outer_hash['detail'] = inner_hash_array.sort_by{ |inner_hash| inner_hash['detail2']}
118
+ outer_hash_array << outer_hash
119
+ end
120
+ self.print_report(options, outer_hash_array, "RightScale MCI ServerTemplate Report")
121
+ end
122
+
123
+ def print_report(options, outer_hash_array, report_title)
124
+ if options[:output_type] == "text"
125
+ self.print_text_report(outer_hash_array, report_title)
126
+ elsif options[:output_type] == "xml"
127
+ self.print_xml_report(outer_hash_array, report_title)
128
+ elsif options[:output_type] == "json"
129
+ self.print_json_report(outer_hash_array, report_title)
130
+ else
131
+
132
+ # Default to text report if no type is specified
133
+ self.print_text_report(outer_hash_array, report_title)
134
+ end
135
+ end
136
+
137
+ # This functions prints the formatted report in a text file
138
+ def print_text_report(outer_hash_array, report_title)
139
+ report_file_name = report_title.gsub(/\s+/, "_") + ".txt"
140
+ file_handler = File.open(report_file_name, 'w')
141
+ current_time = Time.new
142
+
143
+ file_handler.puts sprintf("%75s", "******** #{report_title} ********")
144
+ file_handler.puts ""
145
+ file_handler.puts "Reported generated at: " + current_time.strftime("%m/%d/%Y %H:%M:%S %Z")
146
+ file_handler.puts ""
147
+
148
+ outer_hash_array.each do |outer_hash|
149
+ file_handler.puts '-'*100
150
+ file_handler.puts outer_hash['title']
151
+ file_handler.puts '-'*100
152
+ inner_hash_array = outer_hash['detail']
153
+ title = inner_hash_array[0]
154
+ if title
155
+ title_str = sprintf("%-20s", title['detail1']) + sprintf("%-65s", title['detail2']) + sprintf("%-30s", title['detail3'])
156
+ title_line_str = sprintf("%-20s", '-'*title['detail1'].size) + sprintf("%-65s", '-'*title['detail2'].size) + sprintf("%-30s", '-'*title['detail3'].size)
157
+ file_handler.puts " " + title_str
158
+ file_handler.puts " " + title_line_str
159
+ inner_hash_array.delete(title)
160
+ end
161
+ inner_hash_array.each do |inner_hash|
162
+ detail_str = sprintf("%-20s", inner_hash['detail1']) + sprintf("%-65s", inner_hash['detail2']) + sprintf("%-30s", inner_hash['detail3'])
163
+ file_handler.puts " " + detail_str
164
+ end
165
+ file_handler.puts ""
166
+ end
167
+ end
168
+
169
+ def print_xml_report(outer_hash_array, report_title)
170
+ report_file_name = report_title.gsub(/\s+/, "_") + ".xml"
171
+ file_handler = File.open(report_file_name, 'w')
172
+ current_time = Time.new
173
+
174
+ file_handler.puts "<mci_report>"
175
+ file_handler.puts "<report_title>"
176
+ file_handler.puts report_title
177
+ file_handler.puts "</report_title>"
178
+ file_handler.puts "<generated_time>"
179
+ file_handler.puts current_time.strftime("%m/%d/%Y %H:%M:%S %Z")
180
+ file_handler.puts "</generated_time>"
181
+
182
+ outer_hash_array.each do |outer_hash|
183
+ file_handler.puts "<group>"
184
+ file_handler.puts "<title>"
185
+ file_handler.puts outer_hash['title']
186
+ file_handler.puts "</title>"
187
+ inner_hash_array = outer_hash['detail']
188
+ title = inner_hash_array[0]
189
+ if title
190
+ inner_hash_array.delete(title)
191
+ inner_hash_array.each do |inner_hash|
192
+ detail_str = sprintf("%-20s", inner_hash['detail1']) + sprintf("%-65s", inner_hash['detail2']) + sprintf("%-30s", inner_hash['detail3'])
193
+ file_handler.puts "<#{title['detail1'].gsub(/\s+/, "_")}>"
194
+ file_handler.puts inner_hash['detail1'].to_s.strip
195
+ file_handler.puts "</#{title['detail1'].gsub(/\s+/, "_")}>"
196
+ file_handler.puts "<#{title['detail2'].gsub(/\s+/, "_")}>"
197
+ file_handler.puts inner_hash['detail2'].to_s.strip
198
+ file_handler.puts "</#{title['detail2'].gsub(/\s+/, "_")}>"
199
+ file_handler.puts "<#{title['detail3'].gsub(/\s+/, "_")}>"
200
+ file_handler.puts inner_hash['detail3'].to_s.strip
201
+ file_handler.puts "</#{title['detail3'].gsub(/\s+/, "_")}>"
202
+ end
203
+ end
204
+ file_handler.puts "</group>"
205
+ end
206
+ file_handler.puts "</mci_report>"
207
+ end
208
+
209
+ def print_json_report(outer_hash_array, report_title)
210
+ report_file_name = report_title.gsub(/\s+/, "_") + ".json"
211
+ File.open(report_file_name,"w") do |f|
212
+ f.write(outer_hash_array.to_json)
213
+ end
214
+ end
215
+ end
216
+
217
+
218
+ # Parse the given options and generate the report accordingly
219
+ options = Trollop::with_standard_exception_handling trollop_parser do
220
+ raise Trollop::HelpNeeded if ARGV.empty?
221
+ trollop_parser.parse ARGV
222
+ end
223
+
224
+ mr = MciReport.new
225
+
226
+ if options[:st_id]
227
+ mr.generate_mci_report(options)
228
+ elsif options[:st_list_file]
229
+ yaml_contents = YAML.load_file(File.expand_path(options[:st_list_file], __FILE__))
230
+ mr.generate_servertemplate_report(options, yaml_contents)
231
+ end
232
+
data/bin/mcicp ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'rest_connection'
5
+ require 'trollop'
6
+ require 'highline/import'
7
+
8
+ options = Trollop::options do
9
+ opt :from, "Copy MCIs from this Server Template (id)", :type => :integer, :required => :true
10
+ opt :to, "Copy MCIs to this Server Template (id)", :type => :integer, :required => :true
11
+ opt :skip, "Skip MCIs matching a certain regex", :type => :string
12
+ opt :nice, "Non-Destructive update of the destination Server Template"
13
+ end
14
+
15
+ class MciCp
16
+ def self.go(options)
17
+ temp1 = ServerTemplate.find(options[:from])
18
+ temp2 = ServerTemplate.find(options[:to])
19
+ from_st = ServerTemplateInternal.new(:href => temp1.href)
20
+ mci_payload = from_st.multi_cloud_images
21
+ to_st = ServerTemplateInternal.new(:href => temp2.href)
22
+ to_delete = to_st.multi_cloud_images
23
+ mci_payload.each do |mci|
24
+ begin
25
+ if options[:skip] and mci['name'] =~ /#{options[:skip]}/
26
+ puts "skipping #{mci['name']}, matches skip criteria"
27
+ else
28
+ to_st.add_multi_cloud_image(mci['href'])
29
+ puts "adding #{mci['name']}"
30
+ end
31
+ rescue => e
32
+ puts "image #{mci['name']} already added, skipping" if ENV['DEBUG']
33
+ to_delete.delete(mci)
34
+ end
35
+ end
36
+ default_mci = mci_payload.find {|d| d['name'] =~ /CentOS.+x64/ and d['name'] !~ /EBS/}
37
+ unless default_mci
38
+ default_mci = mci_payload.first
39
+ end
40
+ puts "setting default mci #{default_mci['name']}"
41
+ to_st.set_default_multi_cloud_image(default_mci['href'])
42
+ unless options[:nice]
43
+ to_delete.each do |mci|
44
+ to_st.delete_multi_cloud_image(mci['href'])
45
+ puts "deleting #{mci['name']}"
46
+ end
47
+ end
48
+ puts 'done.'
49
+ end
50
+ end
51
+
52
+ MciCp.go(options)
data/bin/report_tool ADDED
@@ -0,0 +1,404 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Copyright RightScale, Inc. All rights reserved. All access and use subject to the
4
+ # RightScale Terms of Service available at http://www.rightscale.com/terms.php and,
5
+ # if applicable, other agreements such as a RightScale Master Subscription Agreement.
6
+
7
+ #
8
+ # report_tool: Parses system calls and hint files to return a JSON summary of the running system.
9
+ #
10
+
11
+ require 'rubygems'
12
+ # JSON not in core-ruby.
13
+ require 'json'
14
+ # Platform checking.
15
+ require 'rbconfig'
16
+
17
+ # Monkeypatch to recursively clean empty hashes.
18
+ class Hash
19
+ def rec_delete_empty
20
+ delete_if{|k, v| v.nil? or v.empty? or v.instance_of?(Hash) && v.rec_delete_empty.empty?}
21
+ end
22
+ end
23
+
24
+
25
+ # Borrowed from ohai and modifies to detect hypervisor in rebundles.
26
+ def hyperChecker
27
+ #
28
+ # Modifed by Drew Waranis.
29
+ # RightScale, Inc. 2012
30
+ #
31
+
32
+ #
33
+ # Author:: Thom May (<thom@clearairturbulence.org>)
34
+ # Copyright:: Copyright (c) 2009 Opscode, Inc.
35
+ # License:: Apache License, Version 2.0
36
+ #
37
+ # Licensed under the Apache License, Version 2.0 (the "License");
38
+ # you may not use this file except in compliance with the License.
39
+ # You may obtain a copy of the License at
40
+ #
41
+ # http://www.apache.org/licenses/LICENSE-2.0
42
+ #
43
+ # Unless required by applicable law or agreed to in writing, software
44
+ # distributed under the License is distributed on an "AS IS" BASIS,
45
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
46
+ # See the License for the specific language governing permissions and
47
+ # limitations under the License.
48
+ #
49
+
50
+ virtualization = Hash.new
51
+
52
+ # Try virt-what first.
53
+ if File.exists?("/usr/sbin/virt-what")
54
+ return `/usr/sbin/virt-what`.split("\n").first
55
+ end
56
+
57
+ # Xen
58
+ # /proc/xen is an empty dir for EL6 + Linode Guests
59
+ if File.exists?("/proc/xen"); return "xen"; end
60
+
61
+ # Xen Notes:
62
+ # - cpuid of guests, if we could get it, would also be a clue
63
+ # - may be able to determine if under paravirt from /dev/xen/evtchn (See OHAI-253)
64
+ # - EL6 guests carry a 'hypervisor' cpu flag
65
+ # - Additional edge cases likely should not change the above assumptions
66
+ # but rather be additive - btm
67
+
68
+ # Detect from kernel module
69
+ if File.exists?("/proc/modules")
70
+ if File.read("/proc/modules") =~ /^kvm/; return "vbox"; end
71
+ end
72
+
73
+ # Detect KVM/QEMU from cpuinfo, report as KVM
74
+ # We could pick KVM from 'Booting paravirtualized kernel on KVM' in dmesg
75
+ # 2.6.27-9-server (intrepid) has this / 2.6.18-6-amd64 (etch) does not
76
+ # It would be great if we could read pv_info in the kernel
77
+ # Wait for reply to: http://article.gmane.org/gmane.comp.emulators.kvm.devel/27885
78
+ if File.exists?("/proc/cpuinfo")
79
+ if File.read("/proc/cpuinfo") =~ /QEMU Virtual CPU/; return "kvm"; end
80
+ end
81
+
82
+ # Detect OpenVZ / Virtuozzo.
83
+ # http://wiki.openvz.org/BC_proc_entries
84
+ if File.exists?("/proc/vz"); return "openvz"; end
85
+
86
+ # http://www.dmo.ca/blog/detecting-virtualization-on-linux
87
+ if File.exists?("/usr/sbin/dmidecode")
88
+ dmi_info = `/usr/sbin/dmidecode`
89
+ case dmi_info
90
+ when /Manufacturer: Microsoft/ then return "virtualpc"
91
+ when /Manufacturer: VMware/ then return "vmware"
92
+ when /Manufacturer: Xen/ then return "xen"
93
+ end
94
+ end
95
+
96
+ # Detect Linux-VServer
97
+ if File.exists?("/proc/self/status")
98
+ vxid = File.read("/proc/self/status").match(/^(s_context|VxID): (\d+)$/)
99
+ if vxid and vxid[2] then return "linux-vserver"; end
100
+ end
101
+
102
+ return nil;
103
+ end
104
+ # End hypervisor checker.
105
+
106
+
107
+ # JSON class infrastructure.
108
+
109
+ # Inform parser what platform file is from.
110
+ class OS
111
+ def initialize()
112
+ # If it contains linux then Linux, otherwise Windows (future dev).
113
+ if Config::CONFIG["host_os"] =~ /linux/i
114
+ @os = "linux"
115
+ else
116
+ @os = "windows"
117
+ end
118
+ end
119
+
120
+ def to_hash(*a) {"os" => @os}.rec_delete_empty end
121
+ # Strip empty values
122
+ end
123
+
124
+
125
+ # Linux Standard Base.
126
+ # lsb_release: id (i), description (d), release (r), codename (c) .
127
+ class LSB
128
+ def initialize()
129
+ # Retrieve and split command output.
130
+ lsb = `lsb_release -ircs`.split
131
+
132
+ @id = lsb[0]
133
+ # Called separately to get full description with spaces.
134
+ # Sanitize newline and quotes.
135
+ @description = `lsb_release -ds`.sub("\n",'').gsub("\"",'')
136
+ @release = lsb[1]
137
+ @codename = lsb[2]
138
+ end
139
+
140
+ def to_hash(*a)
141
+ { "lsb" =>
142
+ {"id" => @id,
143
+ "description" => @description,
144
+ "release" => @release,
145
+ "codename" => @codename}
146
+ }.rec_delete_empty
147
+ end
148
+ end
149
+
150
+ # Kernel release name.
151
+ # Retrieved from /boot/grub/grub.conf if it is available,
152
+ # else from menu.lst if is available,
153
+ # else from the vmlinuz filename if it is available,
154
+ # else nil.
155
+ # rec_empty_delete strips empty and nil values.
156
+ class Kern
157
+ def initialize()
158
+ # kernel-release is on the first line beginning with "(optional whitespace)initrd".
159
+ # It is located after the first "-" and should not include an ending ".img", if present.
160
+ if File.exists? "/boot/grub/grub.conf"
161
+ @release = IO.read("/boot/grub/grub.conf").match(/^\s*initrd[^-]*-(.*?)(\.img)?$/)[1]
162
+ elsif File.exists? "/boot/grub/menu.lst"
163
+ @release = IO.read("/boot/grub/menu.lst").match(/^\s*initrd[^-]*-(.*?)(\.img)?$/)[1]
164
+ # As a fallback, get kernel-release from the newest vmlinux filename.
165
+ elsif Dir.entries("/boot/grub/").grep(/vmlinuz*/)
166
+ @release = (Dir.glob("/boot/vmlinuz*").max_by {|f| File.mtime(f)}).match(/vmlinuz[^-]*-(.*)/)[1]
167
+ else
168
+ @release = nil
169
+ end
170
+ end
171
+
172
+ def to_hash(*a)
173
+ {"kernel" =>
174
+ {"release" => @release}
175
+ }.rec_delete_empty
176
+ end
177
+ end
178
+
179
+ # Report the presence (and features they enable) of selected modules.
180
+ class Mods
181
+ def initialize(release)
182
+
183
+ # Cancel if kernel version wasn't determined.
184
+ if release.nil?
185
+ @mods = nil
186
+ end
187
+
188
+ # Manual ist of which modules to check.
189
+ # Also update the case-statment below.
190
+ modules_list = %w"dm-mod xfs"
191
+
192
+ # Set locations based on kernel version.
193
+ kernel_config = "/boot/config-#{release}"
194
+ modules_dir = "/lib/modules/#{release}"
195
+
196
+ # Returned by the function to merge into the Kern hash.
197
+ @mods = Hash.new
198
+
199
+ modules_list.each do |mod|
200
+ case mod
201
+ when "dm-mod"
202
+ # Pretty print name.
203
+ feature = "LVM2 (dm-mod)"
204
+ # Flag name in the kerne's config file.
205
+ flag = "CONFIG_BLK_DEV_DM"
206
+ when "xfs"
207
+ feature = "XFS (xfs)"
208
+ flag = "CONFIG_XFS_FS"
209
+ else
210
+ # If module is not defined here, then skip.
211
+ next
212
+ end
213
+
214
+ # Check the kernel's config file, if it exists.
215
+ if File.exists? "#{kernel_config}"
216
+ # Check what the flag is set to.
217
+ case `cat #{kernel_config} | grep #{flag}=`.chomp.split("=")[1]
218
+ when "y"
219
+ status = "Built-In"
220
+ when "m"
221
+ status = "Module"
222
+ when "n"
223
+ status = "Not Selected"
224
+ end
225
+ # As a backup, check if it is dynamically compiled in /lib/modules .
226
+ elsif `find #{modules_dir} -name "#{mod}.ko"`
227
+ status = "Module"
228
+ else
229
+ status = "Not Available"
230
+ end
231
+ @mods[feature] = status
232
+ end
233
+ end
234
+ # Strips value if nil.
235
+ def to_hash(*a)
236
+ {"modules" => @mods }.rec_delete_empty
237
+ end
238
+ end
239
+
240
+ # List packages on Linux system.
241
+ # Takes the LSB's id as an argument.
242
+ class Packages
243
+ def initialize(id)
244
+ # Prep packages hash
245
+ packs = Hash.new
246
+
247
+ # Linux distro family specific options:
248
+ # Ubuntu = dpkg,
249
+ # CentOS/RHEL = rpm,
250
+ # or exit.
251
+
252
+ case id
253
+ when "Ubuntu"
254
+ # Read packages into a hash.
255
+ `dpkg-query -W`.each_line{ |line|
256
+ col = line.split
257
+ packs[col[0]] = col[1]
258
+ }
259
+
260
+ when "CentOS", /RedHat/
261
+ # Read packages into a hash.
262
+ `rpm -qa --qf "%{NAME}\t%{VERSION}\n"`.each_line{ |line|
263
+ col = line.split
264
+ packs[col[0]] = col[1]
265
+ }
266
+ else
267
+ packs["This distro"] = "is not supported."
268
+ exit
269
+ end
270
+
271
+ # Store in instance variable to extract rightlink version.
272
+ @packages = packs
273
+ end
274
+
275
+ def to_hash(*a)
276
+ { "packages" => @packages }.rec_delete_empty
277
+ end
278
+ end
279
+
280
+
281
+ # Holds RS specific info.
282
+ # Takes hint hash as arg.
283
+ class RightScaleMirror
284
+ def initialize(hint)
285
+ @repo_freezedate = hint["freeze-date"]
286
+ @rubygems_freezedate = hint["freeze-date"]
287
+ @rightlink_version = hint["rightlink-version"]
288
+ end
289
+
290
+ def to_hash(*a)
291
+ {"rightscale-mirror" =>
292
+ {"repo-freezedate" => @repo_freezedate,
293
+ "rubygems-freezedate" => @rubygems_freezedate,
294
+ "rightlink-version" => @rightlink_version
295
+ }
296
+ }.rec_delete_empty
297
+ end
298
+ end
299
+
300
+ # Holds info about the image.
301
+ # MD5 sums added to report_hash in later step.
302
+ # Takes hint hash as arg.
303
+ class Image
304
+ def initialize(hint)
305
+ @build_date = hint["build-date"]
306
+ end
307
+
308
+ def to_hash(*a)
309
+ {"image" =>
310
+ {"build-date" => @build_date}
311
+ }.rec_delete_empty
312
+ end
313
+ end
314
+
315
+ # Holds the type and version of hypervisor.
316
+ # Takes hint hash as arg.
317
+ class Virtualization
318
+ def initialize(hint)
319
+ # If the entry is in the hint file, use it.
320
+ if not hint["hypervisor"].nil?
321
+ @hypervisor = hint["hypervisor"]
322
+ # Checks if report_tool is being chrooted.
323
+ # If not, check for hypervisor.
324
+ # Chomp return character.
325
+ elsif `if [ "$(stat -c %d:%i /)" == "$(stat -c %d:%i /proc/1/root/. 2>/dev/null)" ];
326
+ then echo "true";
327
+ else echo "false";
328
+ fi`.chomp == "true"
329
+ @hypervisor = hyperChecker
330
+ # report_tool is running in a chroot and and the hypervisor cannot be properly determined.
331
+ else
332
+ @hypervisor = nil;
333
+ end
334
+ end
335
+
336
+ def to_hash(*a)
337
+ {"virtualization" =>
338
+ {"hypervisor" => @hypervisor,
339
+ "version" => @version}
340
+ }.rec_delete_empty
341
+ end
342
+ end
343
+
344
+ # Name of the cloud the image is meant for.
345
+ class Cloud
346
+ def initialize()
347
+ # Safely ignores hint if not available.
348
+ if File.exists? "/etc/rightscale.d/cloud"
349
+ @provider = File.open("/etc/rightscale.d/cloud", &:readline)
350
+ else
351
+ @provider = nil
352
+ end
353
+ end
354
+ # Strips value if nil.
355
+ def to_hash(*a)
356
+ {"cloud" =>
357
+ {"provider" => @provider}
358
+ }.rec_delete_empty
359
+ end
360
+ end
361
+
362
+ # End JSON class infrastructure.
363
+
364
+ # Merge JSON of classes into report_hash.
365
+ report_hash = Hash.new
366
+ report_hash.merge!(OS.new)
367
+ # Switch on OS.
368
+ if report_hash["os"] != "linux"
369
+ puts "Windows support is coming... soon."
370
+ exit
371
+ end
372
+
373
+ # And the rest.
374
+ report_hash.merge!(LSB.new)
375
+ report_hash.merge!(Kern.new)
376
+ # Take kernel version as argument.
377
+ report_hash.merge!(Mods.new(report_hash["kernel"]["release"]))
378
+ report_hash.merge!(Cloud.new)
379
+
380
+ # Take platform as argument.
381
+ report_hash.merge!(Packages.new(report_hash["lsb"]["id"]))
382
+
383
+ # Give hint hash.
384
+ if File.exists? "/etc/rightscale.d/rightimage-release.js"
385
+ hint = JSON.parse(File.read("/etc/rightscale.d/rightimage-release.js"))
386
+ # Otherwise give empty hint hash.
387
+ else
388
+ hint = Hash.new
389
+ end
390
+
391
+ # Receive hint.
392
+ report_hash.merge!(RightScaleMirror.new(hint))
393
+ report_hash.merge!(Image.new(hint))
394
+ report_hash.merge!(Virtualization.new(hint))
395
+
396
+ # Print results if flag is set.
397
+ if(ARGV[0] == "print" )
398
+ puts JSON.pretty_generate(report_hash)
399
+ end
400
+
401
+ # Save JSON to /tmp.
402
+ File.open("/tmp/report.js","w") do |f|
403
+ f.write(JSON.pretty_generate(report_hash))
404
+ end