Permalink
Browse files

Added Voldemort Web Manager

  • Loading branch information...
1 parent 3d26d92 commit ba1512e8f7cbd46b308291c0787d07ea982cfd76 @rsumbaly rsumbaly committed Mar 29, 2011
View
@@ -17,6 +17,7 @@ Jay Kreps
Jonathan Traupman
Joshua Tuberville
Kirk True
+Matthew Hayes
Michael R. Head
Mike Frost
Neha Narkhede
@@ -0,0 +1,31 @@
+# Voldemort Web Manager #
+
+The web manager use the Voldemort admin client to expose a web interface to Voldemort cluster management. It supports:
+
+* Creation of new stores using JSON serialization
+* Listing stores
+* Displaying JSON schema for a store
+* Listing subset of entries from a store
+* Changing bootstrap URL through UI
+
+## Requirements ##
+
+First install JRuby. Then run the following command to install the required gems.
+
+sudo jruby -S gem install sinatra emk-sinatra-url-for haml warbler sinatra-reloader
+
+## Building ##
+
+Simply build from the Voldemort root directory using "ant". The web manager is written in Ruby but it needs the Voldemort JARs.
+
+## Running ##
+
+Run the following command in the web-manager directory and point your browser to localhost:4567.
+
+jruby init.rb
+
+## Deploying ##
+
+The web manager can be packaged as a WAR using the following command from the web-manager directory:
+
+jruby -S warble
@@ -0,0 +1,95 @@
+require 'rubygems'
+require 'sinatra'
+require 'haml'
+
+include Java
+
+get '/' do
+ redirect url_for '/stores'
+end
+
+get '/stores' do
+ begin
+ proxy = VoldemortAdmin::AdminProxy.new(@bootstrap_host, @bootstrap_port)
+ @stores = proxy.stores
+ rescue
+ end
+ unless @stores.nil?
+ @stores.sort!
+ haml :index
+ else
+ haml :bad_url
+ end
+end
+
+get '/store/:name' do |name|
+ @name = name
+ proxy = VoldemortAdmin::AdminProxy.new(@bootstrap_host, @bootstrap_port)
+ @store = proxy.store(name)
+ halt 404 unless @store
+ @limit = 25
+ fetch_count = @limit + 1
+ @entries = proxy.entries(name, fetch_count)
+ @has_more = @entries.size >= fetch_count
+ @entries = @entries.take(@limit)
+ haml :store
+end
+
+include_class Java::voldemort.client.SocketStoreClientFactory
+include_class Java::voldemort.client.ClientConfig
+
+get '/store/:name/:key' do |name, key|
+ config = ClientConfig.new
+ config.setBootstrapUrls("tcp://" + @bootstrap_url)
+ factory = SocketStoreClientFactory.new(config)
+ client = factory.getStoreClient(name)
+
+ proxy = VoldemortAdmin::AdminProxy.new(@bootstrap_host, @bootstrap_port)
+ @store = proxy.store(name)
+ key_schema = @store.key_info.schema
+
+ # TODO: This only supports keys which are int32 or strings. Figure out how a string
+ # can be passed from the browser and converted to the appropriate type before calling
+ # client.getValue.
+ if (key_schema =~ /int32/)
+ client.getValue(java.lang.Integer.new(key.to_i)).to_s
+ else
+ client.getValue(key).to_s
+ end
+end
+
+get '/stores/new' do
+ haml :store_new
+end
+
+require 'app/helpers/VoldemortAdmin'
+
+post '/stores/new' do
+ key_info = VoldemortAdmin::SerializerInfo.new
+ key_info.name = params[:store_key_name]
+ key_info.schema = params[:store_key_schema]
+
+ value_info = VoldemortAdmin::SerializerInfo.new
+ value_info.name = params[:store_value_name]
+ value_info.schema = params[:store_value_schema]
+
+ store_info = VoldemortAdmin::StoreInfo.new
+ store_info.name = params[:store_name]
+ store_info.key_info = key_info
+ store_info.value_info = value_info
+
+ proxy = VoldemortAdmin::AdminProxy.new(@bootstrap_host, @bootstrap_port)
+ proxy.create_store(store_info)
+
+ redirect url_for '/stores'
+end
+
+get '/config' do
+ haml :config
+end
+
+post '/config' do
+ session["bootstrap_host"] = params["bootstrap_host"]
+ session["bootstrap_port"] = params["bootstrap_port"]
+ redirect url_for '/stores'
+end
@@ -0,0 +1,140 @@
+require 'rubygems'
+
+include Java
+
+include_class Java::voldemort.serialization.SerializerDefinition
+include_class Java::voldemort.store.StoreDefinition
+include_class Java::voldemort.client.RoutingTier
+include_class Java::voldemort.client.protocol.admin.AdminClient
+include_class Java::voldemort.client.protocol.admin.AdminClientConfig
+include_class Java::voldemort.client.protocol.admin.filter.DefaultVoldemortFilter
+include_class Java::voldemort.serialization.DefaultSerializerFactory
+
+module VoldemortAdmin
+
+ class StoreInfo
+ attr_accessor :name, :key_info, :value_info
+
+ def self.from_def(store_def)
+ info = StoreInfo.new
+ info.name = store_def.name
+ info.key_info = SerializerInfo.from_def(store_def.key_serializer)
+ info.value_info = SerializerInfo.from_def(store_def.value_serializer)
+ info
+ end
+
+ def <=>(other)
+ self.name <=> other.name
+ end
+ end
+
+ class SerializerInfo
+ attr_accessor :name, :schema
+
+ def self.from_def(serializer_def)
+ info = SerializerInfo.new
+ info.name = serializer_def.name
+ info.schema = serializer_def.current_schema_info if serializer_def.has_schema_info
+ info
+ end
+ end
+
+ class AdminProxy
+ attr_reader :host, :port
+
+ def initialize(host, port)
+ @host = host
+ @port = port
+ end
+
+ def url
+ @host + ":" + @port
+ end
+
+ def create_store(store_info)
+ key_def = SerializerDefinition.new(store_info.key_info.name, store_info.key_info.schema)
+ value_def = SerializerDefinition.new(store_info.value_info.name, store_info.value_info.schema)
+
+ store_def = StoreDefinition.new(
+ store_info.name,
+ "bdb", #type
+ key_def,
+ value_def,
+ RoutingTier::CLIENT,
+ "consistent-routing", #routingStrategyType
+ 1, #replicationFactor
+ 1, #preferredReads
+ 1, #requiredReads
+ 1, #preferredWrites
+ 1, #requiredWrites
+ nil, #viewOfStore
+ nil, #valTrans
+ nil, #zoneReplicationFactor
+ nil, #zoneCountReads
+ nil, #zoneCountWrites
+ nil, #retentionDays
+ nil #retentionThrottleRate
+ )
+
+ client.add_store(store_def)
+ end
+
+ def store(name)
+ store = stores.find { |store| store.name == name }
+ store
+ end
+
+ def entries(name, max=nil)
+ defs = client.remote_store_def_list(0).value
+ partitions = cluster.node_by_id(0).partition_ids
+ store_def = defs.find { |d| d.name == name }
+ result = []
+ if store_def
+ key_serializer = store_def.key_serializer
+ value_serializer = store_def.value_serializer
+
+ entries = client.fetch_entries(0, name, partitions, DefaultVoldemortFilter.new, false)
+
+ begin
+ has_any = entries.has_next?
+ rescue
+ end
+
+ if !has_any.nil? && has_any
+ while entries.has_next?
+ break if !max.nil? and result.size >= max
+
+ entry = entries.next
+ key_bytes = entry.first.get
+ value_bytes = entry.second.value
+
+ factory = DefaultSerializerFactory.new
+
+ key = factory.get_serializer(key_serializer).to_object(key_bytes)
+ value = factory.get_serializer(value_serializer).to_object(value_bytes)
+
+ result << [key, value]
+ end
+ end
+ end
+ result
+ end
+
+ def stores
+ defs = client.remote_store_def_list(0).value
+ defs.map do |store_def|
+ StoreInfo.from_def(store_def)
+ end
+ end
+
+ private
+
+ def cluster
+ @cluster ||= client.admin_client_cluster
+ end
+
+ def client
+ @client ||= AdminClient.new("tcp://" + url, AdminClientConfig.new)
+ end
+ end
+end
@@ -0,0 +1,4 @@
+%p
+ There seems to be a problem accessing Voldemort. Try
+ %a{:href => (url_for "/config")} switching
+ to a different instance.
@@ -0,0 +1,15 @@
+%form{:id=>"config_form", :method=>"post", :action => (url_for "/config")}
+ %h4 Change the Voldemort instance to connect to
+
+ %p
+ %label Host:
+ %br
+ %input{:id=>"bootstrap_host", :name=>"bootstrap_host", :type=>"text", :value=>"#{@bootstrap_host}"}
+
+ %p
+ %label Port:
+ %br
+ %input{:id=>"bootstrap_port", :name=>"bootstrap_port", :type=>"text", :value=>"#{@bootstrap_port}"}
+
+ %p
+ %input{:type=>"submit", :value=>"Update"}
@@ -0,0 +1,9 @@
+%div
+ %a{:href => (url_for "/stores/new")} Create new store
+
+%h3 List of stores
+
+%div
+ - @stores.each do |store|
+ %a{:href => (url_for "/store/#{store.name}")}= store.name
+ %br
@@ -0,0 +1,18 @@
+%html
+ %head
+ %title Voldemort Cluster Management
+ %link{:rel => "stylesheet", :href => (url_for "/css/common.css"), :type => "text/css"}
+ %script{:src => "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"}
+ %script{:src => (url_for "/script/common.js")}
+ %body
+ #header
+ #title
+ %h1
+ %a{:href => (url_for "/stores")} Voldemort Cluster Management
+ %p
+ Bootstrap URL:
+ = @bootstrap_url
+ %a{:href => (url_for "/config")} Change
+ #outer
+ #main
+ = yield
Oops, something went wrong.

0 comments on commit ba1512e

Please sign in to comment.