visor-web 0.0.1

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/visor-web ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # VISoR Meta command line interface script.
4
+ # Run <visor-meta -h> to get more usage help.
5
+
6
+ require File.expand_path('../../lib/visor-web', __FILE__)
7
+
8
+ #Visor::Meta::CLI.new(Visor::Meta::Server, 'visor-web').run!
9
+ Visor::Web::App.run! :port => 4569
data/lib/visor-web.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'visor-common'
2
+
3
+ $:.unshift File.expand_path('../../lib', __FILE__)
4
+ require 'web/meta'
5
+ require 'web/application/app'
6
+
@@ -0,0 +1,116 @@
1
+ require 'sinatra/base'
2
+ require "google_visualr"
3
+
4
+ module Visor
5
+ module Web
6
+ class App < Sinatra::Base
7
+
8
+ set :static, true
9
+ set :public_folder, File.expand_path('..', __FILE__)
10
+ set :views, File.expand_path('../views', __FILE__)
11
+
12
+ configure :development do
13
+ require 'sinatra/reloader'
14
+ register Sinatra::Reloader
15
+ end
16
+
17
+ helpers do
18
+ WIDTH = 450
19
+ HEIGHT = 350
20
+
21
+ def stores_pie
22
+ client = Visor::Web::Meta.new
23
+ images = client.get_images
24
+
25
+ s3 = cumulus = walrus = hdfs = http = fs = 0
26
+ images.each do |img|
27
+ s3 += img[:store] == 's3' ? 1 : 0
28
+ cumulus += img[:store] == 'cumulus' ? 1 : 0
29
+ walrus += img[:store] == 'walrus' ? 1 : 0
30
+ hdfs += img[:store] == 'hdfs' ? 1 : 0
31
+ http += img[:store] == 'http' ? 1 : 0
32
+ fs += img[:store] == 'fs' ? 1 : 0
33
+ end
34
+
35
+ data_table = GoogleVisualr::DataTable.new
36
+ data_table.new_column('string', 'OS')
37
+ data_table.new_column('number', 'Number of Images')
38
+ data_table.add_rows(6)
39
+ data_table.set_cell(0, 0, 'S3')
40
+ data_table.set_cell(0, 1, s3)
41
+ data_table.set_cell(1, 0, 'Cumulus')
42
+ data_table.set_cell(1, 1, cumulus)
43
+ data_table.set_cell(2, 0, 'Walrus')
44
+ data_table.set_cell(2, 1, walrus)
45
+ data_table.set_cell(3, 0, 'HDFS')
46
+ data_table.set_cell(3, 1, hdfs)
47
+ data_table.set_cell(4, 0, 'HTTP')
48
+ data_table.set_cell(4, 1, http)
49
+ data_table.set_cell(5, 0, 'FS')
50
+ data_table.set_cell(5, 1, fs)
51
+
52
+ opts = {:width => WIDTH, :height => HEIGHT}
53
+ GoogleVisualr::Interactive::PieChart.new(data_table, opts)
54
+ end
55
+
56
+
57
+ def top_distros
58
+ client = Visor::Web::Meta.new
59
+ images = client.get_images
60
+
61
+ ubuntu = rhel = fedora = centos = suse = others = 0
62
+ images.each do |img|
63
+ case img[:name]
64
+ when /ubuntu/i
65
+ ubuntu += 1
66
+ when /redhat/i || /rhel/i
67
+ rhel += 1
68
+ when /fedora/i
69
+ fedora += 1
70
+ when /centos/i
71
+ centos += 1
72
+ when /suse/i
73
+ suse += 1
74
+ else
75
+ others += 1
76
+ end
77
+ end
78
+
79
+ data_table = GoogleVisualr::DataTable.new
80
+ #data_table.new_column('number', 'Ubuntu')
81
+ #data_table.new_column('number', 'RHEL')
82
+ #data_table.new_column('number', 'Fedora')
83
+ #data_table.new_column('number', 'CentOS')
84
+ data_table.new_column('string', 'Distro')
85
+ data_table.new_column('number', 'Quantity')
86
+ data_table.add_rows(6)
87
+ data_table.set_cell(0, 0, 'Ubuntu')
88
+ data_table.set_cell(0, 1, ubuntu)
89
+ data_table.set_cell(1, 0, 'RHEL')
90
+ data_table.set_cell(1, 1, rhel)
91
+ data_table.set_cell(2, 0, 'Fedora')
92
+ data_table.set_cell(2, 1, fedora)
93
+ data_table.set_cell(3, 0, 'CentOS')
94
+ data_table.set_cell(3, 1, centos)
95
+ data_table.set_cell(4, 0, 'SUSE')
96
+ data_table.set_cell(4, 1, suse)
97
+ data_table.set_cell(5, 0, 'Others')
98
+ data_table.set_cell(5, 1, others)
99
+
100
+ opts = {:width => WIDTH, :height => HEIGHT}
101
+ GoogleVisualr::Interactive::ColumnChart.new(data_table, opts)
102
+ end
103
+
104
+
105
+ end
106
+
107
+ get '/' do
108
+ @title = 'Home'
109
+ @pie = stores_pie
110
+ @bars = top_distros
111
+ erb :index
112
+ end
113
+ end
114
+ end
115
+ end
116
+
data/lib/web/meta.rb ADDED
@@ -0,0 +1,313 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'uri'
4
+ require 'json'
5
+
6
+ module Visor
7
+ module Web
8
+
9
+ # The Client API for the VISoR Meta. This class supports all image metadata manipulation
10
+ # operations through a programmatically interface.
11
+ #
12
+ # After Instantiate a Client object its possible to directly interact with the meta server and its
13
+ # database backend.
14
+ #
15
+ class Meta
16
+
17
+ include Visor::Common::Exception
18
+
19
+ configs = Common::Config.load_config :visor_meta
20
+
21
+ DEFAULT_HOST = configs[:bind_host] || '0.0.0.0'
22
+ DEFAULT_PORT = configs[:bind_port] || 4567
23
+
24
+ attr_reader :host, :port, :ssl
25
+
26
+ # Initializes a new new VISoR Meta Client.
27
+ #
28
+ # @option opts [String] :host (DEFAULT_HOST) The host address where VISoR meta server resides.
29
+ # @option opts [String] :port (DEFAULT_PORT) The host port where VISoR meta server resides.
30
+ # @option opts [String] :ssl (false) If the connection should be made through HTTPS (SSL).
31
+ #
32
+ # @example Instantiate a client with default values:
33
+ # client = Visor::Meta::Client.new
34
+ #
35
+ # @example Instantiate a client with default values and SSL enabled:
36
+ # client = Visor::Meta::Client.new(ssl: true)
37
+ #
38
+ # @example Instantiate a client with custom host and port:
39
+ # client = Visor::Meta::Client.new(host: '127.0.0.1', port: 3000)
40
+ #
41
+ def initialize(opts = {})
42
+ @host = opts[:host] || DEFAULT_HOST
43
+ @port = opts[:port] || DEFAULT_PORT
44
+ @ssl = opts[:ssl] || false
45
+ end
46
+
47
+ # Retrieves brief metadata of all public images.
48
+ # Options for filtering the returned results can be passed in.
49
+ #
50
+ # @option query [String] :attribute The image attribute value to filter returned results.
51
+ # @option query [String] :sort ("_id") The image attribute to sort returned results.
52
+ # @option query [String] :dir ("asc") The direction to sort results ("asc"/"desc").
53
+ #
54
+ # @example Retrieve all public images brief metadata:
55
+ # client.get_images
56
+ #
57
+ # # returns:
58
+ # [<all images brief metadata>]
59
+ #
60
+ # @example Retrieve all public 32bit images brief metadata:
61
+ # client.get_images(architecture: 'i386')
62
+ #
63
+ # # returns something like:
64
+ # [
65
+ # {:_id => "28f94e15...", :architecture => "i386", :name => "Fedora 16"},
66
+ # {:_id => "8cb55bb6...", :architecture => "i386", :name => "Ubuntu 11.10 Desktop"}
67
+ # ]
68
+ #
69
+ # @example Retrieve all public 64bit images brief metadata, descending sorted by their name:
70
+ # client.get_images(architecture: 'x86_64', sort: 'name', dir: 'desc')
71
+ #
72
+ # # returns something like:
73
+ # [
74
+ # {:_id => "5e47a41e...", :architecture => "x86_64", :name => "Ubuntu 10.04 Server"},
75
+ # {:_id => "069320f0...", :architecture => "x86_64", :name => "CentOS 6"}
76
+ # ]
77
+ #
78
+ # @return [Array] All public images brief metadata.
79
+ # Just {Visor::Meta::Backends::Base::BRIEF BRIEF} fields are returned.
80
+ #
81
+ # @raise [NotFound] If there are no public images registered on the server.
82
+ #
83
+ def get_images(query = {})
84
+ str = build_query(query)
85
+ request = Net::HTTP::Get.new("/images#{str}")
86
+ do_request(request)
87
+ end
88
+
89
+ # Retrieves detailed metadata of all public images.
90
+ #
91
+ # Filtering and querying works the same as with {#get_images}. The only difference is the number
92
+ # of disclosed attributes.
93
+ #
94
+ # @option query [String] :attribute The image attribute value to filter returned results.
95
+ # @option query [String] :sort ("_id") The image attribute to sort returned results.
96
+ # @option query [String] :dir ("asc") The direction to sort results ("asc"/"desc").
97
+ #
98
+ # @example Retrieve all public images detailed metadata:
99
+ # # request for it
100
+ # client.get_images_detail
101
+ # # returns an array of hashes with all public images metadata.
102
+ #
103
+ # @return [Array] All public images detailed metadata.
104
+ # The {Visor::Meta::Backends::Base::DETAIL_EXC DETAIL_EXC} fields are excluded from results.
105
+ #
106
+ # @raise [NotFound] If there are no public images registered on the server.
107
+ #
108
+ def get_images_detail(query = {})
109
+ str = build_query(query)
110
+ request = Net::HTTP::Get.new("/images/detail#{str}")
111
+ do_request(request)
112
+ end
113
+
114
+ # Retrieves detailed image metadata of the image with the given id.
115
+ #
116
+ # @param id [String] The wanted image's _id.
117
+ #
118
+ # @example Retrieve the image metadata with _id value:
119
+ # # wanted image _id
120
+ # id = "5e47a41e-7b94-4f65-824e-28f94e15bc6a"
121
+ # # ask for that image metadata
122
+ # client.get_image(id)
123
+ #
124
+ # # return example:
125
+ # {
126
+ # :_id => "2cceffc6-ebc5-4741-9653-745524e7ac30",
127
+ # :name => "Ubuntu 10.10",
128
+ # :architecture => "x86_64",
129
+ # :access => "public",
130
+ # :uri => "http://0.0.0.0:4567/images/2cceffc6-ebc5-4741-9653-745524e7ac30",
131
+ # :format => "iso",
132
+ # :status => "available",
133
+ # :store => "file"
134
+ # }
135
+ #
136
+ # @return [Hash] The requested image metadata.
137
+ #
138
+ # @raise [NotFound] If image not found.
139
+ #
140
+ def get_image(id)
141
+ request = Net::HTTP::Get.new("/images/#{id}")
142
+ do_request(request)
143
+ end
144
+
145
+ # Register a new image on the server with the given metadata and returns its metadata.
146
+ #
147
+ # @param meta [Hash] The image metadata.
148
+ #
149
+ # @example Insert a sample image metadata:
150
+ # # sample image metadata
151
+ # meta = {name: 'example', architecture: 'i386', access: 'public'}
152
+ # # insert the new image metadata
153
+ # client.post_image(meta)
154
+ #
155
+ # # returns:
156
+ # { :_id=>"2373c3e5-b302-4529-8e23-c4ffc85e7613",
157
+ # :name=>"example",
158
+ # :architecture=>"i386",
159
+ # :access=>"public",
160
+ # :uri=>"http://0.0.0.0:4567/images/2373c3e5-b302-4529-8e23-c4ffc85e7613",
161
+ # :status=>"locked",
162
+ # :created_at=>"2011-12-13 19:19:26 UTC" }
163
+ #
164
+ # @return [Hash] The already inserted image metadata.
165
+ #
166
+ # @raise [Invalid] If image meta validation fails.
167
+ #
168
+ def post_image(meta)
169
+ request = Net::HTTP::Post.new('/images')
170
+ request.body = prepare_body(meta)
171
+ do_request(request)
172
+ end
173
+
174
+ # Updates an image record with the given metadata and returns its metadata.
175
+ #
176
+ # @param id [String] The image's _id which will be updated.
177
+ # @param meta [Hash] The image metadata.
178
+ #
179
+ # @example Update a sample image metadata:
180
+ # # wanted image _id
181
+ # id = "2373c3e5-b302-4529-8e23-c4ffc85e7613"
182
+ # # update the image metadata with some new values
183
+ # client.put_image(id, name: 'update example')
184
+ #
185
+ # # returns:
186
+ # { :_id=>"2373c3e5-b302-4529-8e23-c4ffc85e7613",
187
+ # :name=>"update example",
188
+ # :architecture=>"i386",
189
+ # :access=>"public",
190
+ # :uri=>"http://0.0.0.0:4567/images/2373c3e5-b302-4529-8e23-c4ffc85e7613",
191
+ # :status=>"locked",
192
+ # :created_at=>"2011-12-13 19:19:26 UTC",
193
+ # :updated_at=>"2011-12-13 19:24:37 +0000" }
194
+ #
195
+ # @return [Hash] The already updated image metadata.
196
+ #
197
+ # @raise [Invalid] If image meta validation fails.
198
+ # @raise [NotFound] If required image was not found.
199
+ #
200
+ def put_image(id, meta)
201
+ request = Net::HTTP::Put.new("/images/#{id}")
202
+ request.body = prepare_body(meta)
203
+ do_request(request)
204
+ end
205
+
206
+ # Removes an image record based on its _id and returns its metadata.
207
+ #
208
+ # @param id [String] The image's _id which will be deleted.
209
+ #
210
+ # @example Delete an image metadata:
211
+ # # wanted image _id
212
+ # id = "2373c3e5-b302-4529-8e23-c4ffc85e7613"
213
+ # # delete the image metadata, which returns it as it was before deletion
214
+ # client.delete_image(id)
215
+ #
216
+ # @return [Hash] The already deleted image metadata. This is useful for recover on accidental delete.
217
+ #
218
+ # @raise [NotFound] If required image was not found.
219
+ #
220
+ def delete_image(id)
221
+ request = Net::HTTP::Delete.new("/images/#{id}")
222
+ do_request(request)
223
+ end
224
+
225
+ private
226
+
227
+ # Parses a response body with the JSON parser and extracts and returns a single
228
+ # key value from it if defined, otherwise returns all the body.
229
+ #
230
+ # @param key (nil) [Symbol] The hash key to extract the wanted value.
231
+ # @param response [Net::HTTPResponse] The response which contains the body to parse.
232
+ #
233
+ # @return [String, Hash] If key is provided and exists on the response body, them return
234
+ # its value, otherwise return all the body hash.
235
+ #
236
+ def parse(key=nil, response)
237
+ parsed = JSON.parse(response.body, symbolize_names: true)
238
+ key ? parsed[key] : parsed
239
+ end
240
+
241
+ # Generate a valid URI query string from key/value pairs of the given hash.
242
+ #
243
+ # @param opts [Hash] The hash with the key/value pairs to generate query from.
244
+ #
245
+ # @return [String] The generated query in the form of "?k=v&k1=v1".
246
+ #
247
+ def build_query(opts)
248
+ opts.empty? ? '' : '?' + URI.encode_www_form(opts)
249
+ end
250
+
251
+ # Fill common header keys before each request. This sets the 'User-Agent' and 'Accept'
252
+ # headers for every request and additionally sets the 'content-type' header
253
+ # for POST and PUT requests.
254
+ #
255
+ # @param request [Net::HTTPResponse] The request which will be modified in its headers.
256
+ #
257
+ def prepare_headers(request)
258
+ request['User-Agent'] = 'VISoR meta server'
259
+ request['Accept'] = 'application/json'
260
+ request['content-type'] = 'application/json' if ['POST', 'PUT'].include?(request.method)
261
+ end
262
+
263
+ # Generate a valid JSON request body for POST and PUT requests.
264
+ # It generates a JSON object encapsulated inside a :image key and then returns it.
265
+ #
266
+ # @param hash [Hash] The hash with the key/value pairs to generate a JSON object from.
267
+ #
268
+ # @return [Hash] If an :image key is already present in the hash, it just returns the plain
269
+ # JSON object, otherwise, encapsulate the hash inside a :image key and returns it.
270
+ #
271
+ def prepare_body(hash)
272
+ hash.has_key?(:image) ? meta.to_json : {image: hash}.to_json
273
+ end
274
+
275
+ # Process requests by preparing its headers, launch them and assert or raise their response.
276
+ #
277
+ # @param request [Net::HTTPResponse] The request which will be launched.
278
+ #
279
+ # @return [String, Hash] If an error is raised, then it parses and returns its message,
280
+ # otherwise it properly parse and return the response body.
281
+ #
282
+ # @raise [NotFound] If required image was not found (on a GET, PUT or DELETE request).
283
+ # @raise [Invalid] If image meta validation fails (on a POST or PUT request).
284
+ #
285
+ def do_request(request)
286
+ prepare_headers(request)
287
+ response = http_or_https.request(request)
288
+ case response
289
+ when Net::HTTPNotFound then
290
+ raise NotFound, parse(:message, response)
291
+ when Net::HTTPBadRequest then
292
+ raise Invalid, parse(:message, response)
293
+ else
294
+ parse(:image, response) or parse(:images, response)
295
+ end
296
+ end
297
+
298
+ # Generate a new HTTP or HTTPS connection based on initialization parameters.
299
+ #
300
+ # @return [Net::HTTP] A HTTP or HTTPS (not done yet) connection ready to use.
301
+ #
302
+ def http_or_https
303
+ if @ssl
304
+ #TODO: ssl connection
305
+ #https://github.com/augustl/net-http-cheat-sheet/blob/master/ssl_and_https.rb
306
+ else
307
+ Net::HTTP.new(@host, @port)
308
+ end
309
+ end
310
+
311
+ end
312
+ end
313
+ end
@@ -0,0 +1,5 @@
1
+ module Visor
2
+ module Web
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: visor-web
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - João Pereira
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rack-test
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: yard
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: sinatra
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: thin
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: google_visualr
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: The VISOR Web System, a Web application dashboard with VISOR statistical
111
+ information.
112
+ email: joaodrp@gmail.com
113
+ executables:
114
+ - visor-web
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - lib/visor-web.rb
119
+ - lib/web/application/app.rb
120
+ - lib/web/meta.rb
121
+ - lib/web/version.rb
122
+ - bin/visor-web
123
+ homepage: http://cvisor.org
124
+ licenses: []
125
+ post_install_message: ! '
126
+
127
+
128
+ ****************************** VISOR ******************************
129
+
130
+
131
+ visor-web was successfully installed!
132
+
133
+
134
+ Generate the VISOR configuration file for this machine (if not already done) by
135
+ running the ''visor-config'' command.
136
+
137
+
138
+ *******************************************************************
139
+
140
+
141
+ '
142
+ rdoc_options: []
143
+ require_paths:
144
+ - lib
145
+ required_ruby_version: !ruby/object:Gem::Requirement
146
+ none: false
147
+ requirements:
148
+ - - ! '>='
149
+ - !ruby/object:Gem::Version
150
+ version: 1.9.2
151
+ required_rubygems_version: !ruby/object:Gem::Requirement
152
+ none: false
153
+ requirements:
154
+ - - ! '>='
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ requirements: []
158
+ rubyforge_project:
159
+ rubygems_version: 1.8.24
160
+ signing_key:
161
+ specification_version: 3
162
+ summary: ! 'VISOR: Virtual Images Management Service for Cloud Infrastructures'
163
+ test_files: []
164
+ has_rdoc: