View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.backup.impl;
20  
21  import java.io.IOException;
22  import java.util.List;
23  
24  import org.apache.commons.cli.CommandLine;
25  import org.apache.commons.lang.StringUtils;
26  import org.apache.hadoop.conf.Configuration;
27  import org.apache.hadoop.conf.Configured;
28  import org.apache.hadoop.hbase.HBaseConfiguration;
29  import org.apache.hadoop.hbase.TableName;
30  import org.apache.hadoop.hbase.backup.BackupInfo;
31  import org.apache.hadoop.hbase.backup.BackupRequest;
32  import org.apache.hadoop.hbase.backup.BackupType;
33  import org.apache.hadoop.hbase.backup.impl.BackupRestoreConstants.BackupCommand;
34  import org.apache.hadoop.hbase.backup.util.BackupClientUtil;
35  import org.apache.hadoop.hbase.backup.util.BackupSet;
36  import org.apache.hadoop.hbase.classification.InterfaceAudience;
37  import org.apache.hadoop.hbase.classification.InterfaceStability;
38  import org.apache.hadoop.hbase.client.Admin;
39  import org.apache.hadoop.hbase.client.BackupAdmin;
40  import org.apache.hadoop.hbase.client.Connection;
41  import org.apache.hadoop.hbase.client.ConnectionFactory;
42  
43  import com.google.common.collect.Lists;
44  
45  /**
46   * General backup commands, options and usage messages
47   */
48  @InterfaceAudience.Private
49  @InterfaceStability.Evolving
50  public final class BackupCommands {
51  
52    private static final String USAGE = "Usage: hbase backup COMMAND\n"
53        + "where COMMAND is one of:\n" 
54        + "  create     create a new backup image\n"
55        + "  cancel     cancel an ongoing backup\n"
56        + "  delete     delete an existing backup image\n"
57        + "  describe   show the detailed information of a backup image\n"
58        + "  history    show history of all successful backups\n"
59        + "  progress   show the progress of the latest backup request\n"
60        + "  set        backup set management\n"
61        + "Enter \'help COMMAND\' to see help message for each command\n";
62  
63    private static final String CREATE_CMD_USAGE =
64        "Usage: hbase backup create <type> <backup_root_path> [tables] [-s name] [-convert] "
65            + "[-silent] [-w workers][-b bandwith]\n" + " type          \"full\" to create a full backup image;\n"
66            + "               \"incremental\" to create an incremental backup image\n"
67            + "  backup_root_path   The full root path to store the backup image,\n"
68            + "                    the prefix can be hdfs, webhdfs or gpfs\n" + " Options:\n"
69            + "  tables      If no tables (\"\") are specified, all tables are backed up. "
70            + "Otherwise it is a\n" + "               comma separated list of tables.\n"
71            + " -w          number of parallel workers.\n" 
72            + " -b          bandwith per one worker (in MB sec)\n" 
73            + " -set        name of backup set" ;
74  
75    private static final String PROGRESS_CMD_USAGE = "Usage: hbase backup progress <backupId>\n"
76        + " backupId      backup image id;\n";
77  
78    private static final String DESCRIBE_CMD_USAGE = "Usage: hbase backup decsribe <backupId>\n"
79        + " backupId      backup image id\n";
80  
81    private static final String HISTORY_CMD_USAGE = "Usage: hbase backup history [-n N]\n"
82        + " -n N     show up to N last backup sessions, default - 10;\n";
83  
84    private static final String DELETE_CMD_USAGE = "Usage: hbase backup delete <backupId>\n"
85        + " backupId      backup image id;\n";
86  
87    private static final String CANCEL_CMD_USAGE = "Usage: hbase backup cancel <backupId>\n"
88        + " backupId      backup image id;\n";
89  
90    private static final String SET_CMD_USAGE = "Usage: hbase backup set COMMAND [name] [tables]\n"
91        + " name       Backup set name\n"
92        + " tables      If no tables (\"\") are specified, all tables will belong to the set. "
93        + "Otherwise it is a\n" + "               comma separated list of tables.\n"
94        + "where COMMAND is one of:\n" 
95        + "  add      add tables to a set, crete set if needed\n"
96        + "  remove   remove tables from set\n"
97        + "  list     list all sets\n"
98        + "  describe describes set\n"
99        + "  delete   delete backup set\n";
100 
101   public static abstract class Command extends Configured {
102     Command(Configuration conf) {
103       super(conf);
104     }
105     public abstract void execute() throws IOException;
106   }
107 
108   private BackupCommands() {
109     throw new AssertionError("Instantiating utility class...");
110   }
111 
112   public static Command createCommand(Configuration conf, BackupCommand type, CommandLine cmdline) {
113     Command cmd = null;
114     switch (type) {
115     case CREATE:
116       cmd = new CreateCommand(conf, cmdline);
117       break;
118     case DESCRIBE:
119       cmd = new DescribeCommand(conf, cmdline);
120       break;
121     case PROGRESS:
122       cmd = new ProgressCommand(conf, cmdline);
123       break;
124     case DELETE:
125       cmd = new DeleteCommand(conf, cmdline);
126       break;
127     case CANCEL:
128       cmd = new CancelCommand(conf, cmdline);
129       break;
130     case HISTORY:
131       cmd = new HistoryCommand(conf, cmdline);
132       break;
133     case SET:
134       cmd = new BackupSetCommand(conf, cmdline);
135       break;
136     case HELP:
137     default:
138       cmd = new HelpCommand(conf, cmdline);
139       break;
140     }
141     return cmd;
142   }
143 
144 
145   public static class CreateCommand extends Command {
146     CommandLine cmdline;
147 
148     CreateCommand(Configuration conf, CommandLine cmdline) {
149       super(conf);
150       this.cmdline = cmdline;
151     }
152     
153     @Override
154     public void execute() throws IOException {
155       if (cmdline == null || cmdline.getArgs() == null) {
156         System.out.println("ERROR: missing arguments");
157         System.out.println(CREATE_CMD_USAGE);
158         System.exit(-1);
159       }
160       String[] args = cmdline.getArgs();
161       if (args.length < 3 || args.length > 4) {
162         System.out.println("ERROR: wrong number of arguments");
163         System.out.println(CREATE_CMD_USAGE);
164         System.exit(-1);
165       }
166 
167       if (!BackupType.FULL.toString().equalsIgnoreCase(args[1])
168           && !BackupType.INCREMENTAL.toString().equalsIgnoreCase(args[1])) {
169         System.out.println("ERROR: invalid backup type");
170         System.out.println(CREATE_CMD_USAGE);
171         System.exit(-1);
172       }
173 
174       String tables = null;
175       Configuration conf = getConf() != null? getConf(): HBaseConfiguration.create();
176 
177       // Check backup set
178       if (cmdline.hasOption("set")) {
179         String setName = cmdline.getOptionValue("set");
180         tables = getTablesForSet(setName, conf);
181 
182         if (tables == null) throw new IOException("Backup set '" + setName
183           + "' is either empty or does not exist");
184       } else {
185         tables = (args.length == 4) ? args[3] : null;
186       }
187       int bandwidth = cmdline.hasOption('b') ? Integer.parseInt(cmdline.getOptionValue('b')) : -1;
188       int workers = cmdline.hasOption('w') ? Integer.parseInt(cmdline.getOptionValue('w')) : -1;
189     
190       try (Connection conn = ConnectionFactory.createConnection(getConf());
191           Admin admin = conn.getAdmin();
192           BackupAdmin backupAdmin = admin.getBackupAdmin();) {
193         BackupRequest request = new BackupRequest();
194         request.setBackupType(BackupType.valueOf(args[1].toUpperCase()))
195         .setTableList(tables != null?Lists.newArrayList(BackupClientUtil.parseTableNames(tables)): null)
196         .setTargetRootDir(args[2]).setWorkers(workers).setBandwidth(bandwidth);
197         String backupId = backupAdmin.backupTables(request);
198         System.out.println("Backup session "+ backupId+" finished. Status: SUCCESS");
199       } catch (IOException e) {
200         System.out.println("Backup session finished. Status: FAILURE");
201         throw e;
202       }
203     }
204     private String getTablesForSet(String name, Configuration conf)
205         throws IOException {
206       try (final Connection conn = ConnectionFactory.createConnection(conf);
207           final BackupSystemTable table = new BackupSystemTable(conn)) {
208         List<TableName> tables = table.describeBackupSet(name);
209         if (tables == null) return null;
210         return StringUtils.join(tables, BackupRestoreConstants.TABLENAME_DELIMITER_IN_COMMAND);        
211       }
212     }
213   }
214 
215   private static class HelpCommand extends Command {
216     CommandLine cmdline;
217 
218     HelpCommand(Configuration conf, CommandLine cmdline) {
219       super(conf);
220       this.cmdline = cmdline;
221     }
222 
223     @Override
224     public void execute() throws IOException {
225       if (cmdline == null) {
226         System.out.println(USAGE);
227         System.exit(0);
228       }
229 
230       String[] args = cmdline.getArgs();
231       if (args == null || args.length == 0) {
232         System.out.println(USAGE);
233         System.exit(0);
234       }
235 
236       if (args.length != 2) {
237         System.out.println("Only support check help message of a single command type");
238         System.out.println(USAGE);
239         System.exit(0);
240       }
241 
242       String type = args[1];
243 
244       if (BackupCommand.CREATE.name().equalsIgnoreCase(type)) {
245         System.out.println(CREATE_CMD_USAGE);
246       } else if (BackupCommand.DESCRIBE.name().equalsIgnoreCase(type)) {
247         System.out.println(DESCRIBE_CMD_USAGE);
248       } else if (BackupCommand.HISTORY.name().equalsIgnoreCase(type)) {
249         System.out.println(HISTORY_CMD_USAGE);
250       } else if (BackupCommand.PROGRESS.name().equalsIgnoreCase(type)) {
251         System.out.println(PROGRESS_CMD_USAGE);
252       } else if (BackupCommand.DELETE.name().equalsIgnoreCase(type)) {
253         System.out.println(DELETE_CMD_USAGE);
254       } else if (BackupCommand.CANCEL.name().equalsIgnoreCase(type)) {
255         System.out.println(CANCEL_CMD_USAGE);
256       } else if (BackupCommand.SET.name().equalsIgnoreCase(type)) {
257         System.out.println(SET_CMD_USAGE);
258       } else {
259         System.out.println("Unknown command : " + type);
260         System.out.println(USAGE);
261       }
262       System.exit(0);
263     }
264   }
265 
266   private static class DescribeCommand extends Command {
267     CommandLine cmdline;
268 
269     DescribeCommand(Configuration conf, CommandLine cmdline) {
270       super(conf);
271       this.cmdline = cmdline;
272     }
273 
274     @Override
275     public void execute() throws IOException {
276       if (cmdline == null || cmdline.getArgs() == null) {
277         System.out.println("ERROR: missing arguments");
278         System.out.println(DESCRIBE_CMD_USAGE);
279         System.exit(-1);
280       }
281       String[] args = cmdline.getArgs();
282       if (args.length != 2) {
283         System.out.println("ERROR: wrong number of arguments");
284         System.out.println(DESCRIBE_CMD_USAGE);
285         System.exit(-1);
286       }
287 
288       String backupId = args[1];
289       Configuration conf = getConf() != null ? getConf() : HBaseConfiguration.create();
290       try (final Connection conn = ConnectionFactory.createConnection(conf);
291           final BackupAdmin admin = conn.getAdmin().getBackupAdmin();) {
292         BackupInfo info = admin.getBackupInfo(backupId);
293         System.out.println(info.getShortDescription());
294       }
295     }
296   }
297 
298   private static class ProgressCommand extends Command {
299     CommandLine cmdline;
300 
301     ProgressCommand(Configuration conf, CommandLine cmdline) {
302       super(conf);
303       this.cmdline = cmdline;
304     }
305 
306     @Override
307     public void execute() throws IOException {
308       if (cmdline == null || cmdline.getArgs() == null ||
309           cmdline.getArgs().length != 2) {
310         System.out.println("No backup id was specified, "
311             + "will retrieve the most recent (ongoing) sessions");
312       }
313       String[] args = cmdline.getArgs();
314       if (args.length > 2) {
315         System.out.println("ERROR: wrong number of arguments: " + args.length);
316         System.out.println(PROGRESS_CMD_USAGE);
317         System.exit(-1);
318       }
319 
320       String backupId = args == null ? null : args[1];
321       Configuration conf = getConf() != null? getConf(): HBaseConfiguration.create();
322       try(final Connection conn = ConnectionFactory.createConnection(conf); 
323           final BackupAdmin admin = conn.getAdmin().getBackupAdmin();){
324         int progress = admin.getProgress(backupId);
325         if(progress < 0){
326           System.out.println("No info was found for backup id: "+backupId);
327         } else{
328           System.out.println(backupId+" progress=" + progress+"%");
329         }
330       } 
331     }
332   }
333 
334   private static class DeleteCommand extends Command {
335     
336     CommandLine cmdline;
337     DeleteCommand(Configuration conf, CommandLine cmdline) {
338       super(conf);
339       this.cmdline = cmdline;
340     }
341 
342     @Override
343     public void execute() throws IOException {
344       if (cmdline == null || cmdline.getArgs() == null || cmdline.getArgs().length < 2) {
345         System.out.println("No backup id(s) was specified");
346         System.out.println(PROGRESS_CMD_USAGE);
347         System.exit(-1);
348       }
349       String[] args = cmdline.getArgs();
350 
351       String[] backupIds = new String[args.length - 1];
352       System.arraycopy(args, 1, backupIds, 0, backupIds.length);
353       Configuration conf = getConf() != null ? getConf() : HBaseConfiguration.create();
354       try (final Connection conn = ConnectionFactory.createConnection(conf);
355           final BackupAdmin admin = conn.getAdmin().getBackupAdmin();) {
356         int deleted = admin.deleteBackups(args);
357         System.out.println("Deleted " + deleted + " backups. Total requested: " + args.length);
358       }
359 
360     }
361   }
362 
363 // TODO Cancel command  
364   
365   private static class CancelCommand extends Command {
366     CommandLine cmdline;
367 
368     CancelCommand(Configuration conf, CommandLine cmdline) {
369       super(conf);
370       this.cmdline = cmdline;
371     }
372 
373     @Override
374     public void execute() throws IOException {
375       if (cmdline == null || cmdline.getArgs() == null || cmdline.getArgs().length < 2) {
376         System.out.println("No backup id(s) was specified, will use the most recent one");
377       }
378       String[] args = cmdline.getArgs();
379       String backupId = args == null || args.length == 0 ? null : args[1];
380       Configuration conf = getConf() != null ? getConf() : HBaseConfiguration.create();
381       try (final Connection conn = ConnectionFactory.createConnection(conf);
382           final BackupAdmin admin = conn.getAdmin().getBackupAdmin();) {
383         // TODO cancel backup
384       }
385     }
386   }
387 
388   private static class HistoryCommand extends Command {
389     CommandLine cmdline;
390     private final static int DEFAULT_HISTORY_LENGTH = 10;
391     
392     HistoryCommand(Configuration conf, CommandLine cmdline) {
393       super(conf);
394       this.cmdline = cmdline;
395     }
396 
397     @Override
398     public void execute() throws IOException {
399 
400       int n = parseHistoryLength();
401       Configuration conf = getConf() != null? getConf(): HBaseConfiguration.create();
402       try(final Connection conn = ConnectionFactory.createConnection(conf); 
403           final BackupAdmin admin = conn.getAdmin().getBackupAdmin();){
404         List<BackupInfo> history = admin.getHistory(n);
405         for(BackupInfo info: history){
406           System.out.println(info.getShortDescription());
407         }
408       } 
409     }
410 
411     private int parseHistoryLength() {
412       String value = cmdline.getOptionValue("n");
413       if (value == null) return DEFAULT_HISTORY_LENGTH;
414       return Integer.parseInt(value);
415     }
416   }
417 
418   private static class BackupSetCommand extends Command {
419     private final static String SET_ADD_CMD = "add";
420     private final static String SET_REMOVE_CMD = "remove";
421     private final static String SET_DELETE_CMD = "delete";
422     private final static String SET_DESCRIBE_CMD = "describe";
423     private final static String SET_LIST_CMD = "list";
424 
425     CommandLine cmdline;
426 
427     BackupSetCommand(Configuration conf, CommandLine cmdline) {
428       super(conf);
429       this.cmdline = cmdline;
430     }
431 
432     @Override
433     public void execute() throws IOException {
434 
435       // Command-line must have at least one element
436       if (cmdline == null || cmdline.getArgs() == null || cmdline.getArgs().length < 2) {
437         throw new IOException("command line format");
438       }
439       String[] args = cmdline.getArgs();
440       String cmdStr = args[1];
441       BackupCommand cmd = getCommand(cmdStr);
442 
443       switch (cmd) {
444       case SET_ADD:
445         processSetAdd(args);
446         break;
447       case SET_REMOVE:
448         processSetRemove(args);
449         break;
450       case SET_DELETE:
451         processSetDelete(args);
452         break;
453       case SET_DESCRIBE:
454         processSetDescribe(args);
455         break;
456       case SET_LIST:
457         processSetList(args);
458         break;
459       default:
460         break;
461 
462       }
463     }
464 
465     private void processSetList(String[] args) throws IOException {
466       // List all backup set names
467       // does not expect any args
468       Configuration conf = getConf() != null? getConf(): HBaseConfiguration.create();
469       try(final Connection conn = ConnectionFactory.createConnection(conf); 
470           final BackupAdmin admin = conn.getAdmin().getBackupAdmin();){
471         List<BackupSet> list = admin.listBackupSets();
472         for(BackupSet bs: list){
473           System.out.println(bs);
474         }
475       }
476     }
477 
478     private void processSetDescribe(String[] args) throws IOException {
479       if (args == null || args.length != 3) {
480         throw new RuntimeException("Wrong number of args: "+args.length);
481       }
482       String setName = args[2];
483       Configuration conf = getConf() != null? getConf(): HBaseConfiguration.create();
484       try(final Connection conn = ConnectionFactory.createConnection(conf); 
485           final BackupAdmin admin = conn.getAdmin().getBackupAdmin();){
486         BackupSet set = admin.getBackupSet(setName);
487         if(set == null) {
488           System.out.println("Set '"+setName+"' does not exist.");
489         } else{
490           System.out.println(set);
491         }
492       }
493     }
494 
495     private void processSetDelete(String[] args) throws IOException {
496       if (args == null || args.length != 3) {
497         throw new RuntimeException("Wrong number of args");
498       }
499       String setName = args[2];
500       Configuration conf = getConf() != null? getConf(): HBaseConfiguration.create();
501       try(final Connection conn = ConnectionFactory.createConnection(conf); 
502           final BackupAdmin admin = conn.getAdmin().getBackupAdmin();){
503         boolean result = admin.deleteBackupSet(setName);
504         if(result){
505           System.out.println("Delete set "+setName+" OK.");
506         } else{
507           System.out.println("Set "+setName+" does not exist");
508         }
509       }
510     }
511 
512     private void processSetRemove(String[] args) throws IOException {
513       if (args == null || args.length != 4) {
514         throw new RuntimeException("Wrong args");
515       }
516       String setName = args[2];
517       String[] tables = args[3].split(",");
518       Configuration conf = getConf() != null? getConf(): HBaseConfiguration.create();
519       try(final Connection conn = ConnectionFactory.createConnection(conf); 
520           final BackupAdmin admin = conn.getAdmin().getBackupAdmin();){
521         admin.removeFromBackupSet(setName, tables);
522       }
523     }
524 
525     private void processSetAdd(String[] args) throws IOException {
526       if (args == null || args.length != 4) {
527         throw new RuntimeException("Wrong args");
528       }
529       String setName = args[2];
530       String[] tables = args[3].split(",");
531       TableName[] tableNames = new TableName[tables.length];
532       for(int i=0; i < tables.length; i++){
533         tableNames[i] = TableName.valueOf(tables[i]);
534       }
535       Configuration conf = getConf() != null? getConf():HBaseConfiguration.create();
536       try(final Connection conn = ConnectionFactory.createConnection(conf); 
537           final BackupAdmin admin = conn.getAdmin().getBackupAdmin();){
538         admin.addToBackupSet(setName, tableNames);
539       }
540       
541     }
542 
543     private BackupCommand getCommand(String cmdStr) throws IOException {
544       if (cmdStr.equals(SET_ADD_CMD)) {
545         return BackupCommand.SET_ADD;
546       } else if (cmdStr.equals(SET_REMOVE_CMD)) {
547         return BackupCommand.SET_REMOVE;
548       } else if (cmdStr.equals(SET_DELETE_CMD)) {
549         return BackupCommand.SET_DELETE;
550       } else if (cmdStr.equals(SET_DESCRIBE_CMD)) {
551         return BackupCommand.SET_DESCRIBE;
552       } else if (cmdStr.equals(SET_LIST_CMD)) {
553         return BackupCommand.SET_LIST;
554       } else {
555         throw new IOException("Unknown command for 'set' :" + cmdStr);
556       }
557     }
558 
559   }  
560 }