~mvo/apt-clone/lp1309447

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#!/usr/bin/python3
# Copyright (C) 2011 Canonical
#
# Authors:
#  Michael Vogt
#
# 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; version 3.
#
# 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, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

from __future__ import print_function

import argparse
from apt_clone import AptClone


if __name__ == "__main__":

    # command line parser
    parser = argparse.ArgumentParser(description="Clone/restore package info")
    parser.add_argument("--debug", action="store_true", default=False,
                        help="enable debug output")
    subparser = parser.add_subparsers(title="Commands")
    # info
    command = subparser.add_parser(
        "info",
        help="info about a apt-clone archive")
    command.add_argument("source")
    command.set_defaults(command="info")
    # clone
    command = subparser.add_parser(
        "clone",
        help="create a clone-file into <destination>. An alternative source dir can be specified with --source.")
    command.add_argument("--source", default="/",
                         help="The source dir of the system or chroot, usually '/'")
    command.add_argument("destination")
    command.add_argument("--with-dpkg-repack",
                         action="store_true", default=False,
                         help="add no longer downloadable package to the state bundle (that can make it rather big)")
    command.add_argument("--with-dpkg-status",
                         action="store_true", default=False,
                         help="include full copy of dpkg-status file, mostly useful for debugging")
    command.set_defaults(command="clone")
    # restore
    command = subparser.add_parser(
        "restore",
        help="restore a clone file from <source>. An alternative destination can be given with --destination.")
    command.add_argument("source")
    command.add_argument("--destination", default="/")
    command.add_argument("--simulate", action="store_true", default=False)
    command.add_argument(
        "--rewrite-server",
        help="rewrite all URIs in sources.list to the specified url")
    command.set_defaults(command="restore")
    # restore on new distro
    command = subparser.add_parser(
        "restore-new-distro",
        help="restore a clone file from <source> to <destination> and try "\
             "upgrading along the way. this can be used so that the current "\
             "release is cloned, a new install is done and then the old clone "\
             "is installed")
    command.add_argument("source")
    command.add_argument("new_distro_codename")
    command.add_argument("--destination", default="/")
    command.add_argument("--simulate", action="store_true", default=False)
    command.set_defaults(command="restore-new-distro")
    # show-diff
    command = subparser.add_parser(
        "show-diff",
        help="show the difference of a clone file from <source> to the system. "
             "An alternative destination can be given with --destination.")
    command.add_argument("source")
    command.add_argument("--destination", default="/")
    command.set_defaults(command="show-diff")

    # parse
    args = parser.parse_args()
    if not hasattr(args, "command"):
        parser.error("no command specified")
    if args.debug:
        import logging
        logging.basicConfig(level=logging.DEBUG)


    # do the actual work
    clone = AptClone()
    if args.command == "info":
        info = clone.info(args.source)
        print(info)
    if args.command == "clone":
        clone.save_state(args.source, args.destination,
                         args.with_dpkg_repack, args.with_dpkg_status)
        print("not installable: %s" % ", ".join(clone.not_downloadable))
        print("version mismatch: %s" % ", ".join(clone.version_mismatch))
        if not args.with_dpkg_repack:
            print("\nNote that you can use --with-dpkg-repack to include "
                  "those packges in the clone file.")
    elif args.command == "restore":
        if args.simulate:
            miss = clone.simulate_restore_state(args.source)
            print("missing: %s" % ",".join(sorted(list(miss))))
        else:
            clone.restore_state(args.source, args.destination,
                                mirror=args.rewrite_server)
    elif args.command == "show-diff":
        clone.show_diff(args.source, args.destination)
    elif args.command == "restore-new-distro":

        # this is a bit of magic, the idea is that if we clone into a new
        # release and we are on it already we do not want to remove
        # packages because they are probably new defaults pkgs. If however
        # we are not yet on the new release its fine to remove installed
        # pkgs as part of the upgrade
        import lsb_release
        codename = lsb_release.get_lsb_information()["CODENAME"]
        if (args.new_distro_codename and args.new_distro_codename == codename):
            protect_installed = True
        else:
            protect_installed = False

        if args.simulate:
            miss = clone.simulate_restore_state(
                args.source, args.new_distro_codename)
            print("missing: %s" % ",".join(sorted(list(miss))))
        else:
            clone.restore_state(
                args.source, args.destination, args.new_distro_codename, protect_installed)