visor-web 0.0.1

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