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         if (info != null) {
294           System.out.println(info.getShortDescription());
295         } else {
296           System.out.println("ERROR: "+ backupId +" not found");
297         }
298       }
299     }
300   }
301 
302   private static class ProgressCommand extends Command {
303     CommandLine cmdline;
304 
305     ProgressCommand(Configuration conf, CommandLine cmdline) {
306       super(conf);
307       this.cmdline = cmdline;
308     }
309 
310     @Override
311     public void execute() throws IOException {
312       if (cmdline == null || cmdline.getArgs() == null ||
313           cmdline.getArgs().length != 2) {
314         System.out.println("No backup id was specified, "
315             + "will retrieve the most recent (ongoing) sessions");
316       }
317       String[] args = cmdline.getArgs();
318       if (args.length > 2) {
319         System.out.println("ERROR: wrong number of arguments: " + args.length);
320         System.out.println(PROGRESS_CMD_USAGE);
321         System.exit(-1);
322       }
323 
324       String backupId = (args == null || args.length <= 1) ? null : args[1];
325       Configuration conf = getConf() != null? getConf(): HBaseConfiguration.create();
326       try(final Connection conn = ConnectionFactory.createConnection(conf); 
327           final BackupAdmin admin = conn.getAdmin().getBackupAdmin();){
328         int progress = admin.getProgress(backupId);
329         if(progress < 0){
330           System.out.println("No info was found for backup id: "+backupId);
331         } else{
332           System.out.println(backupId+" progress=" + progress+"%");
333         }
334       } 
335     }
336   }
337 
338   private static class DeleteCommand extends Command {
339     
340     CommandLine cmdline;
341     DeleteCommand(Configuration conf, CommandLine cmdline) {
342       super(conf);
343       this.cmdline = cmdline;
344     }
345 
346     @Override
347     public void execute() throws IOException {
348       if (cmdline == null || cmdline.getArgs() == null || cmdline.getArgs().length < 2) {
349         System.out.println("No backup id(s) was specified");
350         System.out.println(PROGRESS_CMD_USAGE);
351         System.exit(-1);
352       }
353       String[] args = cmdline.getArgs();
354 
355       String[] backupIds = new String[args.length - 1];
356       System.arraycopy(args, 1, backupIds, 0, backupIds.length);
357       Configuration conf = getConf() != null ? getConf() : HBaseConfiguration.create();
358       try (final Connection conn = ConnectionFactory.createConnection(conf);
359           final BackupAdmin admin = conn.getAdmin().getBackupAdmin();) {
360         int deleted = admin.deleteBackups(args);
361         System.out.println("Deleted " + deleted + " backups. Total requested: " + args.length);
362       }
363 
364     }
365   }
366 
367 // TODO Cancel command  
368   
369   private static class CancelCommand extends Command {
370     CommandLine cmdline;
371 
372     CancelCommand(Configuration conf, CommandLine cmdline) {
373       super(conf);
374       this.cmdline = cmdline;
375     }
376 
377     @Override
378     public void execute() throws IOException {
379       if (cmdline == null || cmdline.getArgs() == null || cmdline.getArgs().length < 2) {
380         System.out.println("No backup id(s) was specified, will use the most recent one");
381       }
382       String[] args = cmdline.getArgs();
383       String backupId = args == null || args.length == 0 ? null : args[1];
384       Configuration conf = getConf() != null ? getConf() : HBaseConfiguration.create();
385       try (final Connection conn = ConnectionFactory.createConnection(conf);
386           final BackupAdmin admin = conn.getAdmin().getBackupAdmin();) {
387         // TODO cancel backup
388       }
389     }
390   }
391 
392   private static class HistoryCommand extends Command {
393     CommandLine cmdline;
394     private final static int DEFAULT_HISTORY_LENGTH = 10;
395     
396     HistoryCommand(Configuration conf, CommandLine cmdline) {
397       super(conf);
398       this.cmdline = cmdline;
399     }
400 
401     @Override
402     public void execute() throws IOException {
403 
404       int n = parseHistoryLength();
405       Configuration conf = getConf() != null? getConf(): HBaseConfiguration.create();
406       try(final Connection conn = ConnectionFactory.createConnection(conf); 
407           final BackupAdmin admin = conn.getAdmin().getBackupAdmin();){
408         List<BackupInfo> history = admin.getHistory(n);
409         for(BackupInfo info: history){
410           System.out.println(info.getShortDescription());
411         }
412       } 
413     }
414 
415     private int parseHistoryLength() {
416       String value = cmdline.getOptionValue("n");
417       if (value == null) return DEFAULT_HISTORY_LENGTH;
418       return Integer.parseInt(value);
419     }
420   }
421 
422   private static class BackupSetCommand extends Command {
423     private final static String SET_ADD_CMD = "add";
424     private final static String SET_REMOVE_CMD = "remove";
425     private final static String SET_DELETE_CMD = "delete";
426     private final static String SET_DESCRIBE_CMD = "describe";
427     private final static String SET_LIST_CMD = "list";
428 
429     CommandLine cmdline;
430 
431     BackupSetCommand(Configuration conf, CommandLine cmdline) {
432       super(conf);
433       this.cmdline = cmdline;
434     }
435 
436     @Override
437     public void execute() throws IOException {
438 
439       // Command-line must have at least one element
440       if (cmdline == null || cmdline.getArgs() == null || cmdline.getArgs().length < 2) {
441         throw new IOException("command line format");
442       }
443       String[] args = cmdline.getArgs();
444       String cmdStr = args[1];
445       BackupCommand cmd = getCommand(cmdStr);
446 
447       switch (cmd) {
448       case SET_ADD:
449         processSetAdd(args);
450         break;
451       case SET_REMOVE:
452         processSetRemove(args);
453         break;
454       case SET_DELETE:
455         processSetDelete(args);
456         break;
457       case SET_DESCRIBE:
458         processSetDescribe(args);
459         break;
460       case SET_LIST:
461         processSetList(args);
462         break;
463       default:
464         break;
465 
466       }
467     }
468 
469     private void processSetList(String[] args) throws IOException {
470       // List all backup set names
471       // does not expect any args
472       Configuration conf = getConf() != null? getConf(): HBaseConfiguration.create();
473       try(final Connection conn = ConnectionFactory.createConnection(conf); 
474           final BackupAdmin admin = conn.getAdmin().getBackupAdmin();){
475         List<BackupSet> list = admin.listBackupSets();
476         for(BackupSet bs: list){
477           System.out.println(bs);
478         }
479       }
480     }
481 
482     private void processSetDescribe(String[] args) throws IOException {
483       if (args == null || args.length != 3) {
484         throw new RuntimeException("Wrong number of args: "+args.length);
485       }
486       String setName = args[2];
487       Configuration conf = getConf() != null? getConf(): HBaseConfiguration.create();
488       try(final Connection conn = ConnectionFactory.createConnection(conf); 
489           final BackupAdmin admin = conn.getAdmin().getBackupAdmin();){
490         BackupSet set = admin.getBackupSet(setName);
491         if(set == null) {
492           System.out.println("Set '"+setName+"' does not exist.");
493         } else{
494           System.out.println(set);
495         }
496       }
497     }
498 
499     private void processSetDelete(String[] args) throws IOException {
500       if (args == null || args.length != 3) {
501         throw new RuntimeException("Wrong number of args");
502       }
503       String setName = args[2];
504       Configuration conf = getConf() != null? getConf(): HBaseConfiguration.create();
505       try(final Connection conn = ConnectionFactory.createConnection(conf); 
506           final BackupAdmin admin = conn.getAdmin().getBackupAdmin();){
507         boolean result = admin.deleteBackupSet(setName);
508         if(result){
509           System.out.println("Delete set "+setName+" OK.");
510         } else{
511           System.out.println("Set "+setName+" does not exist");
512         }
513       }
514     }
515 
516     private void processSetRemove(String[] args) throws IOException {
517       if (args == null || args.length != 4) {
518         throw new RuntimeException("Wrong args");
519       }
520       String setName = args[2];
521       String[] tables = args[3].split(",");
522       Configuration conf = getConf() != null? getConf(): HBaseConfiguration.create();
523       try(final Connection conn = ConnectionFactory.createConnection(conf); 
524           final BackupAdmin admin = conn.getAdmin().getBackupAdmin();){
525         admin.removeFromBackupSet(setName, tables);
526       }
527     }
528 
529     private void processSetAdd(String[] args) throws IOException {
530       if (args == null || args.length != 4) {
531         throw new RuntimeException("Wrong args");
532       }
533       String setName = args[2];
534       String[] tables = args[3].split(",");
535       TableName[] tableNames = new TableName[tables.length];
536       for(int i=0; i < tables.length; i++){
537         tableNames[i] = TableName.valueOf(tables[i]);
538       }
539       Configuration conf = getConf() != null? getConf():HBaseConfiguration.create();
540       try(final Connection conn = ConnectionFactory.createConnection(conf); 
541           final BackupAdmin admin = conn.getAdmin().getBackupAdmin();){
542         admin.addToBackupSet(setName, tableNames);
543       }
544       
545     }
546 
547     private BackupCommand getCommand(String cmdStr) throws IOException {
548       if (cmdStr.equals(SET_ADD_CMD)) {
549         return BackupCommand.SET_ADD;
550       } else if (cmdStr.equals(SET_REMOVE_CMD)) {
551         return BackupCommand.SET_REMOVE;
552       } else if (cmdStr.equals(SET_DELETE_CMD)) {
553         return BackupCommand.SET_DELETE;
554       } else if (cmdStr.equals(SET_DESCRIBE_CMD)) {
555         return BackupCommand.SET_DESCRIBE;
556       } else if (cmdStr.equals(SET_LIST_CMD)) {
557         return BackupCommand.SET_LIST;
558       } else {
559         throw new IOException("Unknown command for 'set' :" + cmdStr);
560       }
561     }
562 
563   }  
564 }