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/update_mcis ADDED
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require "rest_connection"
5
+
6
+ # Set this to key in the st_collection that matches the ID
7
+ # of the ServerTemplate you want to replicated MCIs from.
8
+ SOURCE_KEY = :base
9
+
10
+ # Collection of all ServerTemplate IDs to update
11
+ # Staging
12
+ st_collection = {
13
+ :base_rsb => 243993001,
14
+ :lamp_rsb => 243995001,
15
+ :base => 243994001,
16
+ :lamp_mysql_5_1 => 243996001,
17
+ :lamp_mysql_5_5 => 243997001,
18
+ :lamp_trial => 243998001,
19
+ :load_balancer => 243999001,
20
+ :php_appserver => 244000001,
21
+ :rails_passenger => 244001001,
22
+ :tomcat_app => 244002001,
23
+ :db_mysql_5_1 => 244003001,
24
+ :db_mysql_5_5 => 244004001,
25
+ :storage_toolbox => 244005001,
26
+ :sys_dns => 237966001
27
+ }
28
+
29
+ # Production (v13)
30
+ #st_collection = {
31
+ # :base_rsb => 91124,
32
+ # :lamp_rsb => 129529,
33
+ # :base => 104181,
34
+ # :lamp_mysql_5_1 => 106233,
35
+ # :lamp_mysql_5_5 => 226676001,
36
+ # :lamp_trial => 226834001,
37
+ # :load_balancer => 120077,
38
+ # :php_appserver => 27958,
39
+ # :rails_passenger => 139492,
40
+ # :tomcat_app => 122208,
41
+ # :db_mysql_5_1 => 107666,
42
+ # :db_mysql_5_5 => 214130001,
43
+ # :storage_toolbox => 104346
44
+ #}
45
+
46
+ # Display Mapping (useful for verifying collection is okay)
47
+ #st_collection.each { |k,v| puts "\n\n#{k} == #{ServerTemplate.find(v).nickname}\n\n\n" }
48
+
49
+ puts "Rest_connection log set to /tmp/rc.log"
50
+ ENV['REST_CONNECTION_LOG'] = "/tmp/rc.log"
51
+
52
+ # Run mcicp from SOURCE_KEY to collection of servertemplates
53
+ src_id = st_collection[SOURCE_KEY]
54
+ st_collection.each do |dst_key, dst_id|
55
+ next if dst_id == src_id
56
+ puts '-'*25
57
+ puts "Copying MCIs from #{SOURCE_KEY} to #{dst_key}"
58
+ skip_param = dst_key.to_s =~ /mysql_5_5/ ? "--skip Ubuntu" : ""
59
+ bin = File.expand_path("../mcicp",__FILE__)
60
+ cmd = "#{bin} --from #{src_id} --to #{dst_id} #{skip_param}"
61
+ puts cmd
62
+ puts `#{cmd}`
63
+ puts '-'*25
64
+ end
65
+
data/bin/update_s3_index CHANGED
@@ -1,12 +1,38 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+
3
4
  THIS_FILE = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
4
5
  require File.expand_path(File.join(File.dirname(THIS_FILE), '..', 'lib', 's3_html_indexer'))
5
6
 
6
- raise "ERROR: you must define AWS_IMAGE_BUCKET in you environment. " unless ENV["AWS_IMAGE_BUCKET"]
7
7
  raise "ERROR: you must define AWS_ACCESS_KEY_ID in you environment. " unless ENV["AWS_ACCESS_KEY_ID"]
8
8
  raise "ERROR: you must define AWS_SECRET_ACCESS_KEY in you environment. " unless ENV["AWS_SECRET_ACCESS_KEY"]
9
9
 
10
- indexer = RightImageTools::S3HtmlIndexer.new(ENV["AWS_IMAGE_BUCKET"], ENV["AWS_ACCESS_KEY_ID"], ENV["AWS_SECRET_ACCESS_KEY"])
11
- indexer.to_html("index.html")
12
- indexer.upload_index("index.html")
10
+ aws_image_bucket = ARGV.shift || ENV["AWS_IMAGE_BUCKET"]
11
+ raise "ERROR: you must pass in the bucket to generate an index for. pass in all to generate index for all buckets" unless aws_image_bucket
12
+
13
+ if aws_image_bucket.downcase == "all"
14
+ buckets = %w(
15
+ rightscale-rightimage-base
16
+ rightscale-rightimage-base-dev
17
+ rightscale-cloudstack
18
+ rightscale-cloudstack-dev
19
+ rightscale-openstack
20
+ rightscale-openstack-dev
21
+ rightscale-eucalyptus
22
+ rightscale-eucalyptus-dev
23
+ rightscale-ec2
24
+ rightscale-ec2-dev
25
+ rightscale-google
26
+ rightscale-google-dev
27
+ rightscale-azure
28
+ rightscale-azure-dev
29
+ )
30
+ else
31
+ buckets = [aws_image_bucket]
32
+ end
33
+
34
+ buckets.each do |b|
35
+ indexer = RightImageTools::S3HtmlIndexer.new(b, ENV["AWS_ACCESS_KEY_ID"], ENV["AWS_SECRET_ACCESS_KEY"])
36
+ indexer.to_html("index.html")
37
+ indexer.upload_index("index.html")
38
+ end
@@ -0,0 +1,90 @@
1
+ module AzureHelper
2
+ RS_LINUX_ACCOUNTS = %w(
3
+ rightscalewestus
4
+ rightscaleeastus
5
+ rightscaleeastasia
6
+ rightscalesoutheastasia
7
+ rightscalenortheurope
8
+ rightscalewesteurope
9
+ )
10
+ RS_WINDOWS_ACCOUNTS = %w(
11
+ rightscalewestuswin
12
+ rightscaleeastuswin
13
+ rightscaleeastasiawin
14
+ rightscaleseasiawin
15
+ rightscalenortheuropewin
16
+ rightscalewesteuropewin
17
+ )
18
+ AZURE_LINUX_LOCATIONS = {
19
+ 'rightscalewesteurope' => 'West Europe',
20
+ 'rightscalesoutheastasia' => 'Southeast Asia',
21
+ 'rightscaleeastasia' => 'East Asia',
22
+ 'rightscalenortheurope' => 'North Europe',
23
+ 'rightscalewestus' => 'West US',
24
+ 'rightscaleeastus' => 'East US'
25
+ }
26
+ # AZURE_WINDOWS_LOCATIONS = {
27
+ # 'westeuropewin' => 'West Europe',
28
+ # 'seasiawin' => 'Southeast Asia',
29
+ # 'eastasiawin' => 'East Asia',
30
+ # 'northeuropewin' => 'North Europe',
31
+ # 'westuswin' => 'West US',
32
+ # 'eastuswin' => 'East US'
33
+ # }
34
+ def api_dump(api, method_name, *args)
35
+ result = api.__send__(method_name, *args)
36
+ ensure
37
+ puts "--- #{method_name} ---"
38
+ if api.request
39
+ puts "verb: #{api.request.verb}"
40
+ puts "path: #{api.request.path.inspect}"
41
+ puts "req headers: #{api.request.headers.inspect}"
42
+ puts "req body: #{api.request.body.inspect}"
43
+ puts " "
44
+ if api.response
45
+ puts "resp code: #{api.response.code.inspect}"
46
+ puts "resp headers: #{api.response.headers.inspect}"
47
+ puts "resp body: #{api.response.body.inspect}"
48
+ puts " "
49
+ pp result
50
+ end
51
+ puts "==========================================================================="
52
+ end
53
+ end
54
+
55
+ def lookup_password(account)
56
+ credsfile = "~/.azurecreds.yml"
57
+ creds_msg = <<-EOF
58
+ Please set up a #{credsfile} file in the following format:
59
+ <service account name1>: <shared key2>
60
+ <service account name1>: <shared key2>
61
+ EOF
62
+ begin
63
+ creds = YAML.load_file(File.expand_path(credsfile))
64
+ rescue
65
+ puts creds_msg
66
+ exit 1
67
+ end
68
+ password = creds[account]
69
+ unless password
70
+ puts "Can not find creds for account #{account} in file"
71
+ puts creds_msg
72
+ exit 1
73
+ end
74
+ password
75
+ end
76
+
77
+ def parse_url(url)
78
+ uri = URI.parse(url)
79
+ path = uri.path.reverse.chomp("/").reverse
80
+ (container, blob) = path.split("/",2)
81
+ account = uri.host.split(".").first
82
+ # puts "ACCOUNT: #{account}"
83
+ # puts "CONTAINER: #{container}"
84
+ # puts "BLOB NAME: #{blob}"
85
+
86
+ password = lookup_password(account)
87
+ endpoint = "https://#{account}.blob.core.windows.net"
88
+ return [account,password,endpoint,container,blob]
89
+ end
90
+ end
data/lib/mci.rb CHANGED
@@ -46,27 +46,30 @@ module RightImageTools
46
46
  # Adds an image to an mci, if the image is not already attached to the mci
47
47
  #
48
48
  # === Parameters
49
- # image_id(String)
50
- # cloud_id(Number)
51
- # mci_name(String)
49
+ # image_id(String) - rightscale id of the image to add, which will equal
50
+ # the ami-id for amazon and the resource_uid for other clouds
51
+ # cloud_id(Number) - rightscale if of the cloud
52
+ # mci_name(String) - name of the mci to add this image to
53
+ # mci_id(String) - or (optionally) the rightscale id to add this image to
54
+ # instance_type - default instance type of the image, if not supplied
55
+ # can usually guess
52
56
  #
53
57
  # === Return
54
58
  # mci(MultiCloudImageCloudSettingInternal):: new or existing MCI setting
55
59
  def add_image_to_mci(options)
56
- cloud_id = options[:cloud_id].to_i
57
- image_id = options[:image_id]
60
+ cloud_id = options[:cloud_id].to_i
61
+ image_id = options[:image_id].to_s
58
62
  mci_name = options[:name] || options[:mci_name]
59
63
  mci_id = options[:mci_id] || options[:id]
60
64
  description = options[:description]
61
65
 
62
66
  raise ArgumentError, ":cloud_id not supplied" unless cloud_id > 0
63
67
  raise ArgumentError, ":image_id not supplied" unless image_id =~ /./
64
- raise ArgumentError, "MCI name (:name) or id (:mci_id) not supplied" unless (mci_name =~ /./ || mci_id =~ /^\d+$/)
65
- raise ArgumentError, "MCI (:mci_id) supplied but an integer" if mci_id and mci_id !~ /^\d+$/
66
-
67
- image_id = image_id.split("/").last
68
+ raise ArgumentError, "MCI name (:name) or id (:mci_id) not supplied" unless (mci_name =~ /./ || mci_id.to_s =~ /^\d+$/)
69
+ raise ArgumentError, "MCI (:mci_id) supplied but an integer" if mci_id and mci_id.to_s !~ /^\d+$/
68
70
 
69
71
  if api_version(cloud_id) == "1.0"
72
+ image_id = image_id.split("/").last
70
73
  unless image_id =~ /^ami-[0-9a-z]+$/
71
74
  raise ArgumentError, "image_id #{image_id} doesn't look like an amazon ami"
72
75
  end
@@ -77,9 +80,7 @@ module RightImageTools
77
80
  #validate_mci_name(mci_name) unless mci
78
81
  mci = create_mci(cloud_id, mci_name, description) unless mci
79
82
  add_rightlink_tag(mci)
80
- mci_setting = find_or_create_cloud_setting(mci, image_id, cloud_id)
81
-
82
- # Create the MCIs, if they don't exist.
83
+ mci_setting = find_or_create_cloud_setting(mci, image_id, cloud_id, options)
83
84
 
84
85
  return mci.href
85
86
  end
@@ -87,44 +88,75 @@ module RightImageTools
87
88
  def validate_mci_name(name)
88
89
  end
89
90
 
90
- def guess_instance_type(mci_name)
91
- mci_name =~ /i386/ ? "m1.small" : "m1.large"
92
- end
93
-
94
91
  def find_cloud_setting(mci,cloud_id)
95
- mci_setting = nil
96
- mci_setting = mci.multi_cloud_image_cloud_settings.select { |setting| setting.cloud_id.to_i == cloud_id.to_i }.first
92
+ mci.multi_cloud_image_cloud_settings.find { |s| s.cloud_id.to_i == cloud_id.to_i }
97
93
  end
98
94
 
99
- def find_or_create_cloud_setting(mci, image_id, cloud_id)
100
- mci_setting = nil;
95
+ def guess_default_instance_type(cloud_id)
101
96
  if api_version(cloud_id) == "1.0"
102
- mci_setting = find_cloud_setting(mci, cloud_id)
103
- instance_type = guess_instance_type(mci.name)
104
-
105
- if mci_setting
106
- unless mci_setting.image_href.include?(image_id)
107
- @logger.warn("Replacing image for cloud #{cloud_id} for MCI #{mci.rs_id}")
108
- res = mci_setting.destroy
109
- raise "Non success code returned #{res.inspect} " if res.code.to_s !~ /^2\d\d$/
110
- create_cloud_setting(mci, image_id, cloud_id, instance_type)
97
+ return "m1.small"
98
+ else
99
+ names = ["small instance", "1gb server", "nano-h-5", "standard.small", "small", "standard", "n1-standard-1-d", "m1.small", "s2", "1g" ]
100
+ types = McInstanceType.find_all(cloud_id)
101
+ type = types.find { |t| names.include? t.name.downcase }
102
+ raise "Could not guess default instance type for cloud #{cloud_id}" unless type
103
+ return type.href
104
+ end
105
+ end
106
+
107
+ def find_or_create_cloud_setting(mci, image_id, cloud_id, options = {})
108
+ # debugger
109
+ mci_setting = find_cloud_setting(mci, cloud_id)
110
+ instance_type = options[:instance_type] || guess_default_instance_type(cloud_id)
111
+
112
+ if mci_setting
113
+ unless setting_exists?(mci_setting, image_id, cloud_id)
114
+ @logger.warn("Replacing image for cloud #{cloud_id} for MCI #{mci.rs_id}")
115
+ dummy_setting = nil
116
+ if mci.multi_cloud_image_cloud_settings.length <= 1
117
+ dummy_setting = create_dummy_setting(mci, cloud_id)
111
118
  end
112
- else
113
- @logger.info("Adding cloud images to MCI #{mci.href}")
114
- mci_setting = create_cloud_setting(mci, image_id, cloud_id, instance_type)
115
- #returns nil
119
+ res = mci_setting.destroy
120
+ raise "Non success code returned #{res.inspect} " if res.code.to_s !~ /^2\d\d$/
121
+ mci_setting = create_cloud_setting(mci, image_id, cloud_id, instance_type)
122
+ destroy_dummy_setting(dummy_setting) if dummy_setting
116
123
  end
117
- else
118
- raise NotImplementedError, "API 1.5 not supported yet"
124
+ else
125
+ @logger.info("Adding cloud images to MCI #{mci.href}")
126
+ mci_setting = create_cloud_setting(mci, image_id, cloud_id, instance_type)
127
+ #returns nil
119
128
  end
120
129
  mci_setting
121
130
  end
122
131
 
123
- def create_cloud_setting(mci, image_id, cloud_id, instance_type = nil)
132
+ def setting_exists?(mci_setting, image_id, cloud_id)
133
+ if api_version(cloud_id) == "1.0"
134
+ return mci_setting.image_href.include?(image_id)
135
+ else
136
+ image = McImage.find_all(cloud_id.to_i).find { |img| img.resource_uid.to_s == image_id.to_s }
137
+ raise "No image found for cloud #{cloud_id} with id #{image_id}" unless image
138
+ return mci_setting.image == image.href
139
+ end
140
+ end
141
+
142
+ # Create dummy/destroy dummy settings get around a bug in which we have to replace
143
+ # the (lone) setting for an MCI but can't delete it since its the last one left
144
+ # So temporarily add a dummy image to get around the check, then delete it right
145
+ # after
146
+ def create_dummy_setting(mci, cloud_id)
147
+ dummy_cloud_id = cloud_id.to_i == 1 ? 2 : 1
148
+ dummy_image_id = dummy_cloud_id == 1 ? "ami-41814f28" : "ami-f45beff5"
149
+ create_cloud_setting(mci, dummy_image_id, dummy_cloud_id, "m1.small")
150
+ end
151
+
152
+ def destroy_dummy_setting(setting)
153
+ setting.destroy
154
+ end
155
+
156
+ def create_cloud_setting(mci, image_id, cloud_id, instance_type)
124
157
  mci_setting = nil
125
158
  if api_version(cloud_id) == "1.0"
126
159
  # create the setting
127
- #update_connection_settings(MultiCloudImageInternal)
128
160
  image_href = "#{@api_url}/ec2_images/#{image_id}?cloud_id=#{cloud_id}"
129
161
  mci_setting = MultiCloudImageCloudSettingInternal.create(
130
162
  :multi_cloud_image_href => mci.href,
@@ -132,62 +164,55 @@ module RightImageTools
132
164
  :ec2_image_href => image_href,
133
165
  :aws_instance_type => instance_type)
134
166
  else
135
- raise NotImplementedError, "API 1.5 not supported yet"
167
+ image = McImage.find_all(cloud_id.to_i).find { |img| img.resource_uid.to_s == image_id.to_s }
168
+ raise "No image found for cloud #{cloud_id} with id #{image_id}" unless image
169
+ @logger.debug("Found image id #{image_id} with href #{image.href}")
170
+ mci_id = mci.href.split("/").last
171
+ # debugger
172
+ mci_setting = McMultiCloudImageSetting.create(mci_id,
173
+ :cloud_href => "/api/clouds/#{cloud_id}",
174
+ :image_href => image.href,
175
+ :instance_type_href => instance_type)
136
176
  end
137
177
  mci_setting
138
178
  end
139
179
 
140
180
  # return MultiCloudImageInternal, or nil
141
- def find_mci_by_id(cloud_id,mci_id)
142
- if api_version(cloud_id) == "1.0"
143
- existing_mci = MultiCloudImageInternal.find(mci_id.to_i)
144
- else
145
- existing_mci = McMultiCloudImage.find(mci_id.to_i)
146
- end
181
+ def find_mci_by_id(cloud_id, mci_id)
182
+ existing_mci = MultiCloudImage.find(mci_id.to_i)
183
+ existing_mci.find_and_flatten_settings
147
184
  raise ArgumentError, "Could not find #{existing_mci.rs_id}" unless existing_mci
148
185
  @logger.info("Using mci #{existing_mci.rs_id} named #{existing_mci.name}")
149
186
  existing_mci
150
187
  end
151
188
 
152
189
  def find_mci(cloud_id, mci_name)
153
- mcis = []
154
- if api_version(cloud_id) == "1.0"
155
- #update_connection_settings(MultiCloudImage)
156
- mcis = MultiCloudImage.find_all.
157
- select {|n| n.is_head_version && n.name == mci_name }
158
- mci_ids = mcis.map { |m| m.rs_id }
159
- @logger.warn("Found multiple MCIs with name #{mci_name}: #{mci_ids.join(', ')}") if mcis.length > 1
160
- if mcis.length > 0
161
- existing_mci = MultiCloudImageInternal.find(mcis.first.rs_id.to_i)
162
- end
163
- else
164
- # Double filter, find_with_filter is a wildcard ended match
165
- # Also revision 0 is HEAD
166
- #update_connection_settings(McMultiCloudImage)
167
- mcis = McMultiCloudImage.
168
- find_with_filter(:name=>mci_name).
169
- select {|mci| mci.name == mci_name}.
170
- select {|mci| mci.revision == 0}
171
- @logger.warn("Found multiple MCIs with name #{mci_name}") if mcis.length > 1
190
+ mcis = MultiCloudImage.find_all.
191
+ select {|n| n.is_head_version && n.name == mci_name }
192
+ mci_ids = mcis.map { |m| m.rs_id }
193
+ @logger.warn("Found multiple MCIs with name #{mci_name}: #{mci_ids.join(', ')}") if mcis.length > 1
194
+ if mcis.length > 0
172
195
  existing_mci = mcis.first
196
+ existing_mci.find_and_flatten_settings
197
+ @logger.info("Found mci #{existing_mci.rs_id}") if existing_mci
198
+ return existing_mci
199
+ else
200
+ return nil
173
201
  end
174
-
175
- @logger.info("Found mci #{existing_mci.rs_id}") if existing_mci
176
- existing_mci
177
202
  end
178
203
 
179
204
  def create_mci(cloud_id, mci_name, description = nil)
180
205
  description ||= default_description(mci_name, api_version(cloud_id) == "1.0")
181
206
  @logger.info("Creating mci #{mci_name}")
182
- mci = nil
183
207
 
184
- #update_connection_settings(MultiCloudImageInternal)
185
208
  mci = MultiCloudImageInternal.create(
186
209
  :name => mci_name,
187
210
  :description => description)
188
211
 
189
- raise "Could not create MCI" unless mci
190
- mci
212
+ raise "Could not create MCI" unless mci
213
+ mci = MultiCloudImage.find(mci.rs_id.to_i)
214
+ mci.find_and_flatten_settings
215
+ return mci
191
216
  end
192
217
 
193
218
  def add_rightlink_tag(mci)
@@ -202,7 +227,6 @@ module RightImageTools
202
227
  @logger.info "Adding tag #{tag} to #{mci.href}"
203
228
  href = mci.href
204
229
  result = Tag.set(href, ["#{tag}"])
205
- #update_connection_settings(Tag)
206
230
  @logger.debug("Successfully tagged MCI. Code: #{result.code}")
207
231
  rescue Exception => e
208
232
  @logger.error("Failed to tag MCI #{mci.name}! #{e.inspect}")
@@ -1,5 +1,7 @@
1
1
  require 'rubygems'
2
+ #require 'ruby-debug'
2
3
  require 'right_aws'
4
+ require 'pp'
3
5
 
4
6
  module RightImageTools
5
7
 
@@ -8,29 +10,56 @@ module RightImageTools
8
10
  def initialize(bucket, id=nil, key=nil)
9
11
  @s3 = RightAws::S3.new(id, key)
10
12
  @dev_bucket = bucket.downcase.include?("dev")
13
+
14
+ # If the bucket holds base images.
15
+ @base_bucket = bucket.downcase.include?("rightimage-base")
16
+
17
+ # If the bucket holds reports for the public clouds, ec2, Google, or Azure.
18
+ @public_bucket = bucket.downcase =~ /ec2|google|azure/
19
+
20
+ @cloudstack_bucket = bucket.downcase.include?("cloudstack")
11
21
  @bucket = @s3.bucket(bucket)
12
22
  @keys = @bucket.keys
13
23
  @auto_hash = Hash.new{ |h,k| h[k] = Hash.new &h.default_proc }
14
24
  end
15
25
 
16
- def ver(key)
17
- key.match(/v[0-9]+\.[0-9]+\.[0-9]+/).to_s
18
- end
19
-
20
26
  def to_html(filename)
21
27
  # Create hash hierarchy from bucket list
22
- @keys.reject! { |path| path.full_name =~ /index.html/ }
23
28
  #@keys.sort! {|x,y| ver(x.full_name) <=> ver(y.full_name) } #TODO: sort by rightlink version
24
29
  @keys.each do |path|
25
30
 
26
31
  next unless is_public?(path)
32
+
33
+ path_key = path.full_name
34
+ # Ignore index.html and report_viewer.html .
35
+ next if path_key =~ /index.html/
36
+ next if path_key =~ /report_viewer.html/
37
+ path_key.gsub!('xen/','xenserver/')
38
+ path_key.gsub!(/(vmware|esxi)\/(vmware|esxi)\//,'esxi/')
39
+ path_key.gsub!('vmware/','esxi/')
40
+
41
+ #if path_key.split("/").size != 5
42
+ # puts "WARNING: Skipping malformed path: #{path}"
43
+ # next
44
+ #end
45
+
46
+ # Skip files if they are too small to be an image.
47
+ # Except for mega cloud buckets that collect reports.
48
+ if path.size.to_i < 1024*1024*100 and not @public_bucket
49
+ puts "WARNING: Skipping #{path}: it appears to be too small (<100 MB) to be an image"
50
+ next
51
+ end
27
52
 
28
53
  sub = @auto_hash
29
- path.full_name.split( "/" ).each do |dir|
54
+ path_key.split( "/" ).each do |dir|
30
55
  sub = sub[dir]
31
56
  end
32
57
 
33
- sub[:link] = fix_public_endpoint(path.public_link)
58
+ # Retrieve image and report links duple.
59
+ links = fix_public_endpoint(path.public_link)
60
+
61
+ sub[:link] = links[0]
62
+ sub[:report] = links[1]
34
63
  sub[:size] = path.size
35
64
  sub[:date] = path.last_modified.strftime("%m/%d/%Y")
36
65
  sub[:md5sum] = path.e_tag.gsub(/\"/, "")
@@ -48,27 +77,49 @@ module RightImageTools
48
77
  add_style
49
78
  display_dirs(k,v)
50
79
  output("</html>")
80
+ puts "Output written to #{@file.path}"
51
81
  @file.close
52
82
  end
53
83
  end
54
84
 
55
85
  def upload_index(filename)
56
- @bucket.put("index.html", File.open(filename), {}, 'public-read')
86
+ #
87
+ if File.exists?("index.html")
88
+ @bucket.put("index.html", File.open(filename), {}, 'public-read')
89
+ File.delete("index.html")
90
+
91
+ # Upload amalgamated report_viewer.html with index.html.
92
+ Dir.chdir(File.join(File.dirname(File.expand_path(__FILE__)), "..", "ui", "build"))
93
+ @bucket.put("report_viewer.html", File.open("report_viewer.html"), {}, 'public-read')
94
+ else
95
+ # Upload index.html for empty buckets.
96
+ Dir.chdir(File.join(File.dirname(File.expand_path(__FILE__)), "..", "ui", "build"))
97
+ @bucket.put("index.html", File.open("empty_bucket_index.html"), {}, 'public-read')
98
+ puts "Uploaded empty bucket index.html."
99
+ end
57
100
  end
58
101
 
59
102
  private
103
+ def ver(key)
104
+ key.match(/v[0-9]+\.[0-9]+\.[0-9]+/).to_s
105
+ end
60
106
 
61
107
  def is_public?(key)
62
108
  key.refresh
63
- puts "Key: #{key.name}"
109
+ # puts "Key: #{key.name}"
110
+ is_public = false
64
111
  key.grantees.each do |g|
65
- puts "grantee:#{g.name} perms:#{g.perms}"
112
+ # puts "grantee:#{g.name} perms:#{g.perms}"
66
113
  if g.name.to_s == "AllUsers" && g.perms.to_s == "READ"
67
- puts "Is public!!!"
68
- return true
114
+ is_public = true
69
115
  end
70
116
  end
71
- false
117
+ if is_public
118
+ puts "PUBLIC: #{key.name}"
119
+ else
120
+ puts "NOT PUBLIC: #{key.name}"
121
+ end
122
+ return is_public
72
123
  end
73
124
 
74
125
  def display_dirs(k,v,level=0)
@@ -81,19 +132,47 @@ module RightImageTools
81
132
  add_footer(k, level)
82
133
  else
83
134
  output("<tr class='code'>")
84
- output("<td><a href='#{v[:link]}'>#{k}</a></td>")
85
- output("<td>#{v[:size].to_i/(1024*1024)}MB</td>")
86
- output("<td>#{v[:date]}</td>")
87
- output("<td>#{v[:md5sum]}</td>")
135
+ # Link to images stored on S3 bucket.
136
+ if not @public_bucket
137
+ output("<td><a href='#{v[:link]}'>#{k}</a></td>")
138
+ # Otherwise, the bucket only collects reports.
139
+ else
140
+ # Remove '.js.' extension in public cloud buckets.
141
+ kp = k.gsub(/\.js$/,"")
142
+ output("<td>#{kp}</td>")
143
+ end
144
+ # If bucket does not have report key, do not add "notes" link.
145
+ if not v[:report].empty?
146
+ output("<td><a href='#{v[:report]}'>notes</a></td>")
147
+ else
148
+ # Empty 'notes' cell.
149
+ output("<td></td>")
150
+ end
151
+ # Hide fields for public cloud images.
152
+ if not @public_bucket
153
+ output("<td>#{v[:size].to_i/(1024*1024)}MB</td>")
154
+ output("<td>#{v[:date]}</td>")
155
+ output("<td>#{v[:md5sum]}</td>")
156
+ else
157
+ output("<td>#{v[:date]}</td>")
158
+ end
88
159
  output("</tr>")
89
160
  end
90
161
  end
91
-
162
+
163
+ # Refer to "Base Images" or "RightImages" based on bucket.
92
164
  def disclaimer
93
165
  if @dev_bucket
94
- "The following RightImages are currently in RightScale development and testing. They are not supported by RightScale. For RightImages that RightScale supports, please see our <a href='http://rightscale-cloudstack.s3-website-us-west-1.amazonaws.com/index.html'>production images</a>"
166
+ prod_bucket = @bucket.name.sub("-dev","")
167
+ disclaimer = "The following "
168
+ disclaimer << (@base_bucket ? "Base Images" : "RightImages")
169
+ disclaimer << " are currently in RightScale development and testing. They are not supported by RightScale. For "
170
+ disclaimer << (@base_bucket ? "Base Images" : "RightImages")
171
+ disclaimer << " that RightScale supports, please see our <a href='http://#{prod_bucket}.s3.amazonaws.com/index.html'>production images</a>."
95
172
  else
96
- "The following RightImages are tested and supported by RightScale for general use. For further information regarding use of these images with RightScale, please contact your RightScale account manager or sign up for an account at <a href='http://www.rightscale.com'>www.rightscale.com</a>."
173
+ disclaimer = "The following "
174
+ disclaimer << (@base_bucket ? "Base Images" : "RightImages")
175
+ disclaimer << " are tested and supported by RightScale for general use. For further information regarding use of these images with RightScale, please contact your RightScale account manager or sign up for an account at <a href='http://www.rightscale.com'>www.rightscale.com</a>."
97
176
  end
98
177
  end
99
178
 
@@ -110,22 +189,35 @@ module RightImageTools
110
189
  when 1
111
190
  # Hypervisor
112
191
  output("<div class='hypervisor common'>")
113
- output("<h#{level-1}>#{str.upcase} Hypervisor</h#{level-1}>\n")
192
+ # Include "Hypervisor" in subheader if not base image bucket.
193
+ if @base_bucket
194
+ output("<h#{level-1}>#{str.upcase}</h#{level-1}>\n")
195
+ else
196
+ output("<h#{level-1}>#{str.upcase} Hypervisor</h#{level-1}>\n")
197
+ end
114
198
  output("</div>")
115
199
  output("<div class='platform common'>")
116
200
  output("<table style='padding:none; size:90%'>")
117
201
  output("<tr style='border: solid 1px black;'>")
118
202
  output("<th style='text-align:left'>Image</th>")
119
- output("<th>Size</th>")
120
- output("<th>Date</th>")
121
- output("<th>MD5</th>")
203
+ # Leave 'Notes' header empty.
204
+ output("<th></th>")
205
+ if not @public_bucket
206
+ output("<th>Size</th>")
207
+ output("<th>Date</th>")
208
+ output("<th>MD5</th>")
209
+ else
210
+ output("<th>Date</th>")
211
+ end
122
212
  output("</tr>")
123
213
  when 2
124
214
  return
125
215
  # OS
126
216
  when 3
127
217
  return
218
+ # OS Version
128
219
  else
220
+ return
129
221
  # image
130
222
  # output("<div class='platform'>")
131
223
  end
@@ -145,25 +237,55 @@ module RightImageTools
145
237
  when 3
146
238
  return
147
239
  else
240
+ return
148
241
  # output("</div>")
149
242
  end
150
243
  end
151
244
 
152
245
  # https://s3.amazonaws.com/rightscale-cloudstack/foo
153
246
  # to
154
- # https://rightscale-cloudstack.s3.amazonaws.com
247
+ # http://rightscale-cloudstack.s3.amazonaws.com
155
248
  def fix_public_endpoint(link)
249
+ link = link.dup
250
+ # http apparently more reliable than https when importing cloudstack images (w-4839)
251
+ if @cloudstack_bucket
252
+ link.sub!("https","http")
253
+ link.sub!(":443","")
254
+ end
156
255
  link_ary = link.split("/")
157
256
  bucket = link_ary[3]
158
257
  host = link_ary[2]
159
258
  link_ary[2] = "#{bucket}.#{host}"
160
259
  link_ary.delete_at(3)
161
- link_ary.join("/")
260
+ new_link = link_ary.join("/")
261
+
262
+ # Relative link to report.
263
+ link_ary.slice!(1..2)
264
+
265
+
266
+
267
+
268
+ #!!! Change the (usually double) compressed image extension to ".js".
269
+ #if not @public_bucket
270
+ # link_ary[-1].gsub!(/\.[^.]+\.[^.]+$/,'.js')
271
+ #end
272
+
273
+ # If report doesn't exist, return empty string.
274
+ # Relative url begins at link_ary[1].
275
+ if not RightAws::S3::Key.create(@bucket,link_ary.slice(1..-1).join("/").gsub!(/\.[^.]+\.[^.]+$/,'.js')).exists?
276
+ return [new_link , '']
277
+ end
278
+
279
+ # Replace "https://" with image viewer prefix.
280
+ link_ary[0] = 'report_viewer.html?image='
281
+ report_link = link_ary.join("/")
282
+
283
+ # Return image link and report link.
284
+ return [new_link , report_link]
162
285
  end
163
286
 
164
287
  def output(str)
165
288
  @file.write "#{str}\n" if @file
166
- p str
167
289
  end
168
290
 
169
291
  def add_style
@@ -176,14 +298,14 @@ module RightImageTools
176
298
  th { text-align:center }
177
299
 
178
300
  #header {
179
- background:#235186;
180
- padding:2px 20px;
181
- position:relative;
182
- height:25px;
301
+ background:#235186;
302
+ padding:2px 20px;
303
+ position:relative;
304
+ height:25px;
183
305
  }
184
306
 
185
307
  #header #logo {
186
- margin-top:3px;
308
+ margin-top:3px;
187
309
  }
188
310
 
189
311
  .common {
@@ -191,39 +313,39 @@ module RightImageTools
191
313
  margin-left:auto;
192
314
  margin-right:auto;
193
315
  font-weight: bold;
194
- padding:2px 20px;
195
- position:relative;
316
+ padding:2px 20px;
317
+ position:relative;
196
318
  }
197
319
 
198
320
  .bucket {
199
321
  font-size: 24px;
200
322
  color: #F5F2A9;
201
- background:#235186;
202
- padding:10px 30px 20px;
323
+ background:#235186;
324
+ padding:10px 30px 20px;
203
325
  }
204
326
 
205
327
  .version {
206
328
  color: #F5F2A9;
207
- background:#235186;
329
+ background:#235186;
208
330
  }
209
331
 
210
332
  .hypervisor {
211
333
  color: #235186;
212
- background:#F5F2A9;
334
+ background:#F5F2A9;
213
335
  }
214
336
 
215
337
  .platform {
216
- background: #FFFFFF;
217
- width:90%;
218
- margin-left: auto ;
338
+ background: #FFFFFF;
339
+ width:90%;
340
+ margin-left: auto ;
219
341
  margin-right: auto ;
220
342
  position: relative;
221
343
  }
222
344
 
223
345
  .disclaimer {
224
- background: #FFFFFF;
225
- width:90%;
226
- margin-left: auto ;
346
+ background: #FFFFFF;
347
+ width:90%;
348
+ margin-left: auto ;
227
349
  margin-right: auto ;
228
350
  position: relative;
229
351
  border: 2px solid black;
@@ -231,10 +353,10 @@ module RightImageTools
231
353
  }
232
354
 
233
355
  .code {
234
- font-family:'Courier New', monospace;
235
- color: #000000;
236
- position:relative;
237
- left:10px;
356
+ font-family:'Courier New', monospace;
357
+ color: #000000;
358
+ position:relative;
359
+ left:10px;
238
360
  }
239
361
  ")
240
362
  output "</style>"
@@ -245,7 +367,4 @@ module RightImageTools
245
367
  end
246
368
 
247
369
  end
248
- end
249
-
250
-
251
-
370
+ end