HBASE-20276 restore original shell REPL functionality where commands can return results
[hbase.git] / hbase-shell / src / main / ruby / shell.rb
blob2e228f5a061a71988e5e9f240cb028ca7779270a
3 # Licensed to the Apache Software Foundation (ASF) under one
4 # or more contributor license agreements.  See the NOTICE file
5 # distributed with this work for additional information
6 # regarding copyright ownership.  The ASF licenses this file
7 # to you under the Apache License, Version 2.0 (the
8 # "License"); you may not use this file except in compliance
9 # with the License.  You may obtain a copy of the License at
11 #     http://www.apache.org/licenses/LICENSE-2.0
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
20 # Shell commands module
21 module Shell
22   @@commands = {}
23   def self.commands
24     @@commands
25   end
27   @@command_groups = {}
28   def self.command_groups
29     @@command_groups
30   end
32   def self.load_command(name, group, aliases = [])
33     return if commands[name]
35     # Register command in the group
36     raise ArgumentError, "Unknown group: #{group}" unless command_groups[group]
37     command_groups[group][:commands] << name
39     # Load command
40     begin
41       require "shell/commands/#{name}"
42       klass_name = name.to_s.gsub(/(?:^|_)(.)/) { Regexp.last_match(1).upcase } # camelize
43       commands[name] = eval("Commands::#{klass_name}")
44       aliases.each do |an_alias|
45         commands[an_alias] = commands[name]
46       end
47     rescue => e
48       raise "Can't load hbase shell command: #{name}. Error: #{e}\n#{e.backtrace.join("\n")}"
49     end
50   end
52   def self.load_command_group(group, opts)
53     raise ArgumentError, "No :commands for group #{group}" unless opts[:commands]
55     command_groups[group] = {
56       commands: [],
57       command_names: opts[:commands],
58       full_name: opts[:full_name] || group,
59       comment: opts[:comment]
60     }
62     all_aliases = opts[:aliases] || {}
64     opts[:commands].each do |command|
65       aliases = all_aliases[command] || []
66       load_command(command, group, aliases)
67     end
68   end
70   #----------------------------------------------------------------------
71   # rubocop:disable Metrics/ClassLength
72   class Shell
73     attr_accessor :hbase
74     attr_accessor :interactive
75     alias interactive? interactive
77     @debug = false
78     attr_accessor :debug
80     def initialize(hbase, interactive = true)
81       self.hbase = hbase
82       self.interactive = interactive
83     end
85     # Returns Admin class from admin.rb
86     def admin
87       @admin ||= hbase.admin
88     end
90     def hbase_taskmonitor
91       @hbase_taskmonitor ||= hbase.taskmonitor
92     end
94     def hbase_table(name)
95       hbase.table(name, self)
96     end
98     def hbase_replication_admin
99       @hbase_replication_admin ||= hbase.replication_admin
100     end
102     def hbase_security_admin
103       @hbase_security_admin ||= hbase.security_admin
104     end
106     def hbase_visibility_labels_admin
107       @hbase_visibility_labels_admin ||= hbase.visibility_labels_admin
108     end
110     def hbase_quotas_admin
111       @hbase_quotas_admin ||= hbase.quotas_admin
112     end
114     def hbase_rsgroup_admin
115       @rsgroup_admin ||= hbase.rsgroup_admin
116     end
118     def export_commands(where)
119       ::Shell.commands.keys.each do |cmd|
120         # here where is the IRB namespace
121         # this method just adds the call to the specified command
122         # which just references back to 'this' shell object
123         # a decently extensible way to add commands
124         where.send :instance_eval, <<-EOF
125           def #{cmd}(*args)
126             ret = @shell.command('#{cmd}', *args)
127             puts
128             return ret
129           end
130         EOF
131       end
132     end
134     def command_instance(command)
135       ::Shell.commands[command.to_s].new(self)
136     end
138     # call the method 'command' on the specified command
139     def command(command, *args)
140       internal_command(command, :command, *args)
141     end
143     # call a specific internal method in the command instance
144     # command  - name of the command to call
145     # method_name - name of the method on the command to call. Defaults to just 'command'
146     # args - to be passed to the named method
147     def internal_command(command, method_name = :command, *args)
148       command_instance(command).command_safe(debug, method_name, *args)
149     end
151     def print_banner
152       puts 'HBase Shell'
153       puts 'Use "help" to get list of supported commands.'
154       puts 'Use "exit" to quit this interactive shell.'
155       print 'Version '
156       command('version')
157       puts
158     end
160     def help_multi_command(command)
161       puts "Command: #{command}"
162       puts command_instance(command).help
163       puts
164       nil
165     end
167     def help_command(command)
168       puts command_instance(command).help
169       nil
170     end
172     def help_group(group_name)
173       group = ::Shell.command_groups[group_name.to_s]
174       group[:commands].sort.each { |cmd| help_multi_command(cmd) }
175       if group[:comment]
176         puts '-' * 80
177         puts
178         puts group[:comment]
179         puts
180       end
181       nil
182     end
184     def help(command = nil)
185       if command
186         return help_command(command) if ::Shell.commands[command.to_s]
187         return help_group(command) if ::Shell.command_groups[command.to_s]
188         puts "ERROR: Invalid command or command group name: #{command}"
189         puts
190       end
192       puts help_header
193       puts
194       puts 'COMMAND GROUPS:'
195       ::Shell.command_groups.each do |name, group|
196         puts '  Group name: ' + name
197         puts '  Commands: ' + group[:command_names].sort.join(', ')
198         puts
199       end
200       unless command
201         puts 'SHELL USAGE:'
202         help_footer
203       end
204       nil
205     end
207     def help_header
208       "HBase Shell, version #{org.apache.hadoop.hbase.util.VersionInfo.getVersion}, " \
209              "r#{org.apache.hadoop.hbase.util.VersionInfo.getRevision}, " \
210              "#{org.apache.hadoop.hbase.util.VersionInfo.getDate}" + "\n" \
211              "Type 'help \"COMMAND\"', (e.g. 'help \"get\"' -- the quotes are necessary) for help on a specific command.\n" \
212              "Commands are grouped. Type 'help \"COMMAND_GROUP\"', (e.g. 'help \"general\"') for help on a command group."
213     end
215     def help_footer
216       puts <<-HERE
217 Quote all names in HBase Shell such as table and column names.  Commas delimit
218 command parameters.  Type <RETURN> after entering a command to run it.
219 Dictionaries of configuration used in the creation and alteration of tables are
220 Ruby Hashes. They look like this:
222   {'key1' => 'value1', 'key2' => 'value2', ...}
224 and are opened and closed with curley-braces.  Key/values are delimited by the
225 '=>' character combination.  Usually keys are predefined constants such as
226 NAME, VERSIONS, COMPRESSION, etc.  Constants do not need to be quoted.  Type
227 'Object.constants' to see a (messy) list of all constants in the environment.
229 If you are using binary keys or values and need to enter them in the shell, use
230 double-quote'd hexadecimal representation. For example:
232   hbase> get 't1', "key\\x03\\x3f\\xcd"
233   hbase> get 't1', "key\\003\\023\\011"
234   hbase> put 't1', "test\\xef\\xff", 'f1:', "\\x01\\x33\\x40"
236 The HBase shell is the (J)Ruby IRB with the above HBase-specific commands added.
237 For more on the HBase Shell, see http://hbase.apache.org/book.html
238       HERE
239     end
240   end
241   # rubocop:enable Metrics/ClassLength
244 # Load commands base class
245 require 'shell/commands'
247 # Load all commands
248 Shell.load_command_group(
249   'general',
250   full_name: 'GENERAL HBASE SHELL COMMANDS',
251   commands: %w[
252     status
253     version
254     table_help
255     whoami
256     processlist
257   ]
260 Shell.load_command_group(
261   'ddl',
262   full_name: 'TABLES MANAGEMENT COMMANDS',
263   commands: %w[
264     alter
265     create
266     describe
267     disable
268     disable_all
269     is_disabled
270     drop
271     drop_all
272     enable
273     enable_all
274     is_enabled
275     exists
276     list
277     show_filters
278     alter_status
279     alter_async
280     get_table
281     locate_region
282     list_regions
283   ],
284   aliases: {
285     'describe' => ['desc']
286   }
289 Shell.load_command_group(
290   'namespace',
291   full_name: 'NAMESPACE MANAGEMENT COMMANDS',
292   commands: %w[
293     create_namespace
294     drop_namespace
295     alter_namespace
296     describe_namespace
297     list_namespace
298     list_namespace_tables
299   ]
302 Shell.load_command_group(
303   'dml',
304   full_name: 'DATA MANIPULATION COMMANDS',
305   commands: %w[
306     count
307     delete
308     deleteall
309     get
310     get_counter
311     incr
312     put
313     scan
314     truncate
315     truncate_preserve
316     append
317     get_splits
318   ]
321 Shell.load_command_group(
322   'tools',
323   full_name: 'HBASE SURGERY TOOLS',
324   comment: "WARNING: Above commands are for 'experts'-only as misuse can damage an install",
325   commands: %w[
326     assign
327     balancer
328     balance_switch
329     balancer_enabled
330     normalize
331     normalizer_switch
332     normalizer_enabled
333     is_in_maintenance_mode
334     close_region
335     compact
336     flush
337     major_compact
338     move
339     split
340     merge_region
341     unassign
342     zk_dump
343     wal_roll
344     catalogjanitor_run
345     catalogjanitor_switch
346     catalogjanitor_enabled
347     cleaner_chore_run
348     cleaner_chore_switch
349     cleaner_chore_enabled
350     compact_rs
351     compaction_state
352     trace
353     splitormerge_switch
354     splitormerge_enabled
355     clear_compaction_queues
356     list_deadservers
357     clear_deadservers
358     clear_block_cache
359   ],
360   # TODO: remove older hlog_roll command
361   aliases: {
362     'wal_roll' => ['hlog_roll']
363   }
366 Shell.load_command_group(
367   'replication',
368   full_name: 'CLUSTER REPLICATION TOOLS',
369   commands: %w[
370     add_peer
371     remove_peer
372     list_peers
373     enable_peer
374     disable_peer
375     set_peer_replicate_all
376     set_peer_serial
377     set_peer_namespaces
378     append_peer_namespaces
379     remove_peer_namespaces
380     set_peer_exclude_namespaces
381     show_peer_tableCFs
382     set_peer_tableCFs
383     set_peer_exclude_tableCFs
384     set_peer_bandwidth
385     list_replicated_tables
386     append_peer_tableCFs
387     remove_peer_tableCFs
388     enable_table_replication
389     disable_table_replication
390     get_peer_config
391     list_peer_configs
392     update_peer_config
393   ]
396 Shell.load_command_group(
397   'snapshots',
398   full_name: 'CLUSTER SNAPSHOT TOOLS',
399   commands: %w[
400     snapshot
401     clone_snapshot
402     restore_snapshot
403     delete_snapshot
404     delete_all_snapshot
405     delete_table_snapshots
406     list_snapshots
407     list_table_snapshots
408   ]
411 Shell.load_command_group(
412   'configuration',
413   full_name: 'ONLINE CONFIGURATION TOOLS',
414   commands: %w[
415     update_config
416     update_all_config
417   ]
420 Shell.load_command_group(
421   'quotas',
422   full_name: 'CLUSTER QUOTAS TOOLS',
423   commands: %w[
424     set_quota
425     list_quotas
426     list_quota_table_sizes
427     list_quota_snapshots
428     list_snapshot_sizes
429   ]
432 Shell.load_command_group(
433   'security',
434   full_name: 'SECURITY TOOLS',
435   comment: 'NOTE: Above commands are only applicable if running with the AccessController coprocessor',
436   commands: %w[
437     list_security_capabilities
438     grant
439     revoke
440     user_permission
441   ]
444 Shell.load_command_group(
445   'procedures',
446   full_name: 'PROCEDURES & LOCKS MANAGEMENT',
447   commands: %w[
448     abort_procedure
449     list_procedures
450     list_locks
451   ]
454 Shell.load_command_group(
455   'visibility labels',
456   full_name: 'VISIBILITY LABEL TOOLS',
457   comment: 'NOTE: Above commands are only applicable if running with the VisibilityController coprocessor',
458   commands: %w[
459     add_labels
460     list_labels
461     set_auths
462     get_auths
463     clear_auths
464     set_visibility
465   ]
468 Shell.load_command_group(
469   'rsgroup',
470   full_name: 'RSGroups',
471   comment: "NOTE: The rsgroup Coprocessor Endpoint must be enabled on the Master else commands fail with:
472   UnknownProtocolException: No registered Master Coprocessor Endpoint found for RSGroupAdminService",
473   commands: %w[
474     list_rsgroups
475     get_rsgroup
476     add_rsgroup
477     remove_rsgroup
478     balance_rsgroup
479     move_servers_rsgroup
480     move_tables_rsgroup
481     move_namespaces_rsgroup
482     move_servers_tables_rsgroup
483     move_servers_namespaces_rsgroup
484     get_server_rsgroup
485     get_table_rsgroup
486     remove_servers_rsgroup
487   ]