rightimage_tools 0.2.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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