| #!/usr/bin/env python3 |
| # |
| # Manipulations with qcow2 image |
| # |
| # Copyright (C) 2012 Red Hat, Inc. |
| # |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; either version 2 of the License, or |
| # (at your option) any later version. |
| # |
| # This program is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| # |
| |
| import sys |
| |
| from qcow2_format import ( |
| QcowHeader, |
| QcowHeaderExtension |
| ) |
| |
| |
| is_json = False |
| |
| |
| def cmd_dump_header(fd): |
| h = QcowHeader(fd) |
| h.dump(is_json) |
| print() |
| h.dump_extensions(is_json) |
| |
| |
| def cmd_dump_header_exts(fd): |
| h = QcowHeader(fd) |
| h.dump_extensions(is_json) |
| |
| |
| def cmd_set_header(fd, name, value): |
| try: |
| value = int(value, 0) |
| except ValueError: |
| print("'%s' is not a valid number" % value) |
| sys.exit(1) |
| |
| fields = (field[2] for field in QcowHeader.fields) |
| if name not in fields: |
| print("'%s' is not a known header field" % name) |
| sys.exit(1) |
| |
| h = QcowHeader(fd) |
| h.__dict__[name] = value |
| h.update(fd) |
| |
| |
| def cmd_add_header_ext(fd, magic, data): |
| try: |
| magic = int(magic, 0) |
| except ValueError: |
| print("'%s' is not a valid magic number" % magic) |
| sys.exit(1) |
| |
| h = QcowHeader(fd) |
| h.extensions.append(QcowHeaderExtension.create(magic, |
| data.encode('ascii'))) |
| h.update(fd) |
| |
| |
| def cmd_add_header_ext_stdio(fd, magic): |
| data = sys.stdin.read() |
| cmd_add_header_ext(fd, magic, data) |
| |
| |
| def cmd_del_header_ext(fd, magic): |
| try: |
| magic = int(magic, 0) |
| except ValueError: |
| print("'%s' is not a valid magic number" % magic) |
| sys.exit(1) |
| |
| h = QcowHeader(fd) |
| found = False |
| |
| for ex in h.extensions: |
| if ex.magic == magic: |
| found = True |
| h.extensions.remove(ex) |
| |
| if not found: |
| print("No such header extension") |
| return |
| |
| h.update(fd) |
| |
| |
| def cmd_set_feature_bit(fd, group, bit): |
| try: |
| bit = int(bit, 0) |
| if bit < 0 or bit >= 64: |
| raise ValueError |
| except ValueError: |
| print("'%s' is not a valid bit number in range [0, 64)" % bit) |
| sys.exit(1) |
| |
| h = QcowHeader(fd) |
| if group == 'incompatible': |
| h.incompatible_features |= 1 << bit |
| elif group == 'compatible': |
| h.compatible_features |= 1 << bit |
| elif group == 'autoclear': |
| h.autoclear_features |= 1 << bit |
| else: |
| print("'%s' is not a valid group, try " |
| "'incompatible', 'compatible', or 'autoclear'" % group) |
| sys.exit(1) |
| |
| h.update(fd) |
| |
| |
| cmds = [ |
| ['dump-header', cmd_dump_header, 0, |
| 'Dump image header and header extensions'], |
| ['dump-header-exts', cmd_dump_header_exts, 0, |
| 'Dump image header extensions'], |
| ['set-header', cmd_set_header, 2, 'Set a field in the header'], |
| ['add-header-ext', cmd_add_header_ext, 2, 'Add a header extension'], |
| ['add-header-ext-stdio', cmd_add_header_ext_stdio, 1, |
| 'Add a header extension, data from stdin'], |
| ['del-header-ext', cmd_del_header_ext, 1, 'Delete a header extension'], |
| ['set-feature-bit', cmd_set_feature_bit, 2, 'Set a feature bit'], |
| ] |
| |
| |
| def main(filename, cmd, args): |
| fd = open(filename, "r+b") |
| try: |
| for name, handler, num_args, desc in cmds: |
| if name != cmd: |
| continue |
| elif len(args) != num_args: |
| usage() |
| return |
| else: |
| handler(fd, *args) |
| return |
| print("Unknown command '%s'" % cmd) |
| finally: |
| fd.close() |
| |
| |
| def usage(): |
| print("Usage: %s <file> <cmd> [<arg>, ...] [<key>, ...]" % sys.argv[0]) |
| print("") |
| print("Supported commands:") |
| for name, handler, num_args, desc in cmds: |
| print(" %-20s - %s" % (name, desc)) |
| print("") |
| print("Supported keys:") |
| print(" %-20s - %s" % ('-j', 'Dump in JSON format')) |
| |
| |
| if __name__ == '__main__': |
| if len(sys.argv) < 3: |
| usage() |
| sys.exit(1) |
| |
| is_json = '-j' in sys.argv |
| if is_json: |
| sys.argv.remove('-j') |
| |
| main(sys.argv[1], sys.argv[2], sys.argv[3:]) |