Procházet zdrojové kódy

info: add --json option

Marian Beermann před 8 roky
rodič
revize
cc26bdf810
3 změnil soubory, kde provedl 65 přidání a 21 odebrání
  1. 44 14
      src/borg/archiver.py
  2. 11 7
      src/borg/cache.py
  3. 10 0
      src/borg/testsuite/archiver.py

+ 44 - 14
src/borg/archiver.py

@@ -65,6 +65,10 @@ from .upgrader import AtticRepositoryUpgrader, BorgRepositoryUpgrader
 STATS_HEADER = "                       Original size      Compressed size    Deduplicated size"
 
 
+def print_as_json(obj):
+    print(json.dumps(obj, sort_keys=True, indent=4))
+
+
 def argument(args, str_or_bool):
     """If bool is passed, return it. If str is passed, retrieve named attribute from args."""
     if isinstance(str_or_bool, str):
@@ -960,7 +964,7 @@ class Archiver:
         if any((args.location.archive, args.first, args.last, args.prefix)):
             return self._info_archives(args, repository, manifest, key, cache)
         else:
-            return self._info_repository(repository, key, cache)
+            return self._info_repository(args, repository, key, cache)
 
     def _info_archives(self, args, repository, manifest, key, cache):
         def format_cmdline(cmdline):
@@ -998,20 +1002,44 @@ class Archiver:
                 print()
         return self.exit_code
 
-    def _info_repository(self, repository, key, cache):
-        print('Repository ID: %s' % bin_to_hex(repository.id))
-        if key.NAME == 'plaintext':
-            encrypted = 'No'
+    def _info_repository(self, args, repository, key, cache):
+        if args.json:
+            encryption = {
+                'mode': key.NAME,
+            }
+            if key.NAME.startswith('key file'):
+                encryption['keyfile'] = key.find_key()
+        else:
+            encryption = 'Encrypted: '
+            if key.NAME == 'plaintext':
+                encryption += 'No'
+            else:
+                encryption += 'Yes (%s)' % key.NAME
+            if key.NAME.startswith('key file'):
+                encryption += '\nKey file: %s' % key.find_key()
+
+        info = {
+            'id': bin_to_hex(repository.id),
+            'location': repository._location.canonical_path(),
+            'cache': cache.path,
+            'security_dir': cache.security_manager.dir,
+            'encryption': encryption,
+        }
+
+        if args.json:
+            info['cache-stats'] = cache.stats()
+            print_as_json(info)
         else:
-            encrypted = 'Yes (%s)' % key.NAME
-        print('Encrypted: %s' % encrypted)
-        if key.NAME.startswith('key file'):
-            print('Key file: %s' % key.find_key())
-        print('Cache: %s' % cache.path)
-        print('Security dir: %s' % cache.security_manager.dir)
-        print(DASHES)
-        print(STATS_HEADER)
-        print(str(cache))
+            print(textwrap.dedent("""
+            Repository ID: {id}
+            Location: {location}
+            {encryption}
+            Cache: {cache}
+            Security dir: {security_dir}
+            """).strip().format_map(info))
+            print(DASHES)
+            print(STATS_HEADER)
+            print(str(cache))
         return self.exit_code
 
     @with_repository(exclusive=True)
@@ -2542,6 +2570,8 @@ class Archiver:
         subparser.add_argument('location', metavar='REPOSITORY_OR_ARCHIVE', nargs='?', default='',
                                type=location_validator(),
                                help='archive or repository to display information about')
+        subparser.add_argument('--json', action='store_true',
+                               help='format output as JSON')
         self.add_archives_filters_args(subparser)
 
         break_lock_epilog = process_epilog("""

+ 11 - 7
src/borg/cache.py

@@ -219,18 +219,22 @@ All archives:   {0.total_size:>20s} {0.total_csize:>20s} {0.unique_csize:>20s}
 Chunk index:    {0.total_unique_chunks:20d} {0.total_chunks:20d}"""
         return fmt.format(self.format_tuple())
 
-    def format_tuple(self):
+    Summary = namedtuple('Summary', ['total_size', 'total_csize', 'unique_size', 'unique_csize', 'total_unique_chunks',
+                                     'total_chunks'])
+
+    def stats(self):
         # XXX: this should really be moved down to `hashindex.pyx`
-        Summary = namedtuple('Summary', ['total_size', 'total_csize', 'unique_size', 'unique_csize', 'total_unique_chunks', 'total_chunks'])
-        stats = Summary(*self.chunks.summarize())._asdict()
+        stats = self.Summary(*self.chunks.summarize())._asdict()
+        return stats
+
+    def format_tuple(self):
+        stats = self.stats()
         for field in ['total_size', 'total_csize', 'unique_csize']:
             stats[field] = format_file_size(stats[field])
-        return Summary(**stats)
+        return self.Summary(**stats)
 
     def chunks_stored_size(self):
-        Summary = namedtuple('Summary', ['total_size', 'total_csize', 'unique_size', 'unique_csize', 'total_unique_chunks', 'total_chunks'])
-        stats = Summary(*self.chunks.summarize())
-        return stats.unique_csize
+        return self.stats()['unique_csize']
 
     def create(self):
         """Create a new empty cache at `self.path`

+ 10 - 0
src/borg/testsuite/archiver.py

@@ -1112,6 +1112,16 @@ class ArchiverTestCase(ArchiverTestCaseBase):
         info_archive = self.cmd('info', '--first', '1', self.repository_location)
         assert 'Archive name: test\n' in info_archive
 
+    def test_info_json(self):
+        self.create_regular_file('file1', size=1024 * 80)
+        self.cmd('init', '--encryption=repokey', self.repository_location)
+        self.cmd('create', self.repository_location + '::test', 'input')
+        info_repo = json.loads(self.cmd('info', '--json', self.repository_location))
+        assert len(info_repo['id']) == 64
+        assert info_repo['encryption']['mode'] == 'repokey'
+        assert 'keyfile' not in info_repo['encryption']
+        assert 'cache-stats' in info_repo
+
     def test_comment(self):
         self.create_regular_file('file1', size=1024 * 80)
         self.cmd('init', '--encryption=repokey', self.repository_location)