~veger/ubuntu/precise/samba/fix-for-902339

« back to all changes in this revision

Viewing changes to lib/dnspython/examples/zonediff.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2011-12-21 13:18:04 UTC
  • mfrom: (0.39.21 sid)
  • Revision ID: package-import@ubuntu.com-20111221131804-xtlr39wx6njehxxr
Tags: 2:3.6.1-3ubuntu1
* Merge from Debian testing.  Remaining changes:
  + debian/patches/VERSION.patch:
    - set SAMBA_VERSION_SUFFIX to Ubuntu.
  + debian/patches/error-trans.fix-276472:
    - Add the translation of Unix Error code -ENOTSUP to NT Error Code
    - NT_STATUS_NOT_SUPPORTED to prevent the Permission denied error.
  + debian/smb.conf:
    - add "(Samba, Ubuntu)" to server string.
    - comment out the default [homes] share, and add a comment about
      "valid users = %S" to show users how to restrict access to
      \\server\username to only username.
    - Set 'usershare allow guests', so that usershare admins are 
      allowed to create public shares in addition to authenticated
      ones.
    - add map to guest = Bad user, maps bad username to guest access.
  + debian/samba-common.config:
    - Do not change priority to high if dhclient3 is installed.
    - Use priority medium instead of high for the workgroup question.
  + debian/control:
    - Don't build against or suggest ctdb.
    - Add dependency on samba-common-bin to samba.
  + Add ufw integration:
    - Created debian/samba.ufw.profile
    - debian/rules, debian/samba.dirs, debian/samba.files: install
      profile
    - debian/control: have samba suggest ufw
  + Add apport hook:
    - Created debian/source_samba.py.
    - debian/rules, debian/samba.dirs, debian/samba-common-bin.files: install
  + Switch to upstart:
    - Add debian/samba.{nmbd,smbd}.upstart.
  + debian/samba.logrotate, debian/samba-common.dhcp, debian/samba.if-up:
    - Make them upstart compatible
  + debian/samba.postinst: 
    - Avoid scary pdbedit warnings on first import.
  + debian/samba-common.postinst: Add more informative error message for
    the case where smb.conf was manually deleted
  + debian/patches/fix-debuglevel-name-conflict.patch: don't use 'debug_level'
    as a global variable name in an NSS module 
  + Dropped:
    - debian/patches/error-trans.fix-276472
    - debian/patches/fix-debuglevel-name-conflict.patch

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
 
3
# Small library and commandline tool to do logical diffs of zonefiles
 
4
# ./zonediff -h gives you help output
 
5
#
 
6
# Requires dnspython to do all the heavy lifting
 
7
#
 
8
# (c)2009 Dennis Kaarsemaker <dennis@kaarsemaker.net>
 
9
#
 
10
# Permission to use, copy, modify, and distribute this software and its
 
11
# documentation for any purpose with or without fee is hereby granted,
 
12
# provided that the above copyright notice and this permission notice
 
13
# appear in all copies.
 
14
 
15
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 
16
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 
17
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 
18
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 
19
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 
20
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
 
21
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
22
"""See diff_zones.__doc__ for more information"""
 
23
 
 
24
__all__ = ['diff_zones', 'format_changes_plain', 'format_changes_html']
 
25
 
 
26
try:
 
27
    import dns.zone
 
28
except ImportError:
 
29
    import sys
 
30
    sys.stderr.write("Please install dnspython")
 
31
    sys.exit(1)
 
32
 
 
33
def diff_zones(zone1, zone2, ignore_ttl=False, ignore_soa=False):
 
34
    """diff_zones(zone1, zone2, ignore_ttl=False, ignore_soa=False) -> changes
 
35
    Compares two dns.zone.Zone objects and returns a list of all changes
 
36
    in the format (name, oldnode, newnode).
 
37
 
 
38
    If ignore_ttl is true, a node will not be added to this list if the
 
39
    only change is its TTL.
 
40
    
 
41
    If ignore_soa is true, a node will not be added to this list if the
 
42
    only changes is a change in a SOA Rdata set.
 
43
 
 
44
    The returned nodes do include all Rdata sets, including unchanged ones.
 
45
    """
 
46
 
 
47
    changes = []
 
48
    for name in zone1:
 
49
        name = str(name)
 
50
        n1 = zone1.get_node(name)
 
51
        n2 = zone2.get_node(name)
 
52
        if not n2:
 
53
            changes.append((str(name), n1, n2))
 
54
        elif _nodes_differ(n1, n2, ignore_ttl, ignore_soa):
 
55
            changes.append((str(name), n1, n2))
 
56
 
 
57
    for name in zone2:
 
58
        n1 = zone1.get_node(name)
 
59
        if not n1:
 
60
            n2 = zone2.get_node(name)
 
61
            changes.append((str(name), n1, n2))
 
62
    return changes
 
63
 
 
64
def _nodes_differ(n1, n2, ignore_ttl, ignore_soa):
 
65
    if ignore_soa or not ignore_ttl:
 
66
        # Compare datasets directly
 
67
        for r in n1.rdatasets:
 
68
            if ignore_soa and r.rdtype == dns.rdatatype.SOA:
 
69
                continue
 
70
            if r not in n2.rdatasets:
 
71
                return True
 
72
            if not ignore_ttl:
 
73
                return r.ttl != n2.find_rdataset(r.rdclass, r.rdtype).ttl
 
74
 
 
75
        for r in n2.rdatasets:
 
76
            if ignore_soa and r.rdtype == dns.rdatatype.SOA:
 
77
                continue
 
78
            if r not in n1.rdatasets:
 
79
                return True
 
80
    else:
 
81
        return n1 != n2
 
82
 
 
83
def format_changes_plain(oldf, newf, changes, ignore_ttl=False):
 
84
    """format_changes(oldfile, newfile, changes, ignore_ttl=False) -> str
 
85
    Given 2 filenames and a list of changes from diff_zones, produce diff-like
 
86
    output. If ignore_ttl is True, TTL-only changes are not displayed"""
 
87
 
 
88
    ret = "--- %s\n+++ %s\n" % (oldf, newf)
 
89
    for name, old, new in changes:
 
90
        ret +=  "@ %s\n" % name
 
91
        if not old:
 
92
            for r in new.rdatasets:
 
93
                ret += "+ %s\n" % str(r).replace('\n','\n+ ')
 
94
        elif not new:
 
95
            for r in old.rdatasets:
 
96
                ret += "- %s\n" % str(r).replace('\n','\n+ ')
 
97
        else:
 
98
            for r in old.rdatasets:
 
99
                if r not in new.rdatasets or (r.ttl != new.find_rdataset(r.rdclass, r.rdtype).ttl and not ignore_ttl):
 
100
                    ret += "- %s\n" % str(r).replace('\n','\n+ ')
 
101
            for r in new.rdatasets:
 
102
                if r not in old.rdatasets or (r.ttl != old.find_rdataset(r.rdclass, r.rdtype).ttl and not ignore_ttl):
 
103
                    ret += "+ %s\n" % str(r).replace('\n','\n+ ')
 
104
    return ret
 
105
 
 
106
def format_changes_html(oldf, newf, changes, ignore_ttl=False):
 
107
    """format_changes(oldfile, newfile, changes, ignore_ttl=False) -> str
 
108
    Given 2 filenames and a list of changes from diff_zones, produce nice html
 
109
    output. If ignore_ttl is True, TTL-only changes are not displayed"""
 
110
 
 
111
    ret = '''<table class="zonediff">
 
112
  <thead>
 
113
    <tr>
 
114
      <th>&nbsp;</th>
 
115
      <th class="old">%s</th>
 
116
      <th class="new">%s</th>
 
117
    </tr>
 
118
  </thead>
 
119
  <tbody>\n''' % (oldf, newf)
 
120
 
 
121
    for name, old, new in changes:
 
122
        ret +=  '    <tr class="rdata">\n      <td class="rdname">%s</td>\n' % name
 
123
        if not old:
 
124
            for r in new.rdatasets:
 
125
                ret += '      <td class="old">&nbsp;</td>\n      <td class="new">%s</td>\n' % str(r).replace('\n','<br />')
 
126
        elif not new:
 
127
            for r in old.rdatasets:
 
128
                ret += '      <td class="old">%s</td>\n      <td class="new">&nbsp;</td>\n' % str(r).replace('\n','<br />')
 
129
        else:
 
130
            ret += '      <td class="old">'
 
131
            for r in old.rdatasets:
 
132
                if r not in new.rdatasets or (r.ttl != new.find_rdataset(r.rdclass, r.rdtype).ttl and not ignore_ttl):
 
133
                    ret += str(r).replace('\n','<br />')
 
134
            ret += '</td>\n'
 
135
            ret += '      <td class="new">'
 
136
            for r in new.rdatasets:
 
137
                if r not in old.rdatasets or (r.ttl != old.find_rdataset(r.rdclass, r.rdtype).ttl and not ignore_ttl):
 
138
                    ret += str(r).replace('\n','<br />')
 
139
            ret += '</td>\n'
 
140
        ret += '    </tr>\n'
 
141
    return ret + '  </tbody>\n</table>'
 
142
 
 
143
# Make this module usable as a script too.
 
144
if __name__ == '__main__':
 
145
    import optparse
 
146
    import subprocess
 
147
    import sys
 
148
    import traceback
 
149
 
 
150
    usage = """%prog zonefile1 zonefile2 - Show differences between zones in a diff-like format
 
151
%prog [--git|--bzr|--rcs] zonefile rev1 [rev2] - Show differences between two revisions of a zonefile
 
152
 
 
153
The differences shown will be logical differences, not textual differences.
 
154
"""
 
155
    p = optparse.OptionParser(usage=usage)
 
156
    p.add_option('-s', '--ignore-soa', action="store_true", default=False, dest="ignore_soa",
 
157
                 help="Ignore SOA-only changes to records")
 
158
    p.add_option('-t', '--ignore-ttl', action="store_true", default=False, dest="ignore_ttl",
 
159
                 help="Ignore TTL-only changes to Rdata")
 
160
    p.add_option('-T', '--traceback', action="store_true", default=False, dest="tracebacks",
 
161
                 help="Show python tracebacks when errors occur")
 
162
    p.add_option('-H', '--html', action="store_true", default=False, dest="html",
 
163
                 help="Print HTML output")
 
164
    p.add_option('-g', '--git', action="store_true", default=False, dest="use_git",
 
165
                 help="Use git revisions instead of real files")
 
166
    p.add_option('-b', '--bzr', action="store_true", default=False, dest="use_bzr",
 
167
                 help="Use bzr revisions instead of real files")
 
168
    p.add_option('-r', '--rcs', action="store_true", default=False, dest="use_rcs",
 
169
                 help="Use rcs revisions instead of real files")
 
170
    opts, args = p.parse_args()
 
171
    opts.use_vc = opts.use_git or opts.use_bzr or opts.use_rcs
 
172
 
 
173
    def _open(what, err):
 
174
        if isinstance(what, basestring):
 
175
            # Open as normal file
 
176
            try:
 
177
                return open(what, 'rb')
 
178
            except:
 
179
                sys.stderr.write(err + "\n")
 
180
                if opts.tracebacks:
 
181
                    traceback.print_exc()
 
182
        else:
 
183
            # Must be a list, open subprocess
 
184
            try:
 
185
                proc = subprocess.Popen(what, stdout=subprocess.PIPE)
 
186
                proc.wait()
 
187
                if proc.returncode == 0:
 
188
                    return proc.stdout
 
189
                sys.stderr.write(err + "\n")
 
190
            except:
 
191
                sys.stderr.write(err + "\n")
 
192
                if opts.tracebacks:
 
193
                    traceback.print_exc()
 
194
 
 
195
    if not opts.use_vc and len(args) != 2:
 
196
        p.print_help()
 
197
        sys.exit(64)
 
198
    if opts.use_vc and len(args) not in (2,3):
 
199
        p.print_help()
 
200
        sys.exit(64)
 
201
 
 
202
    # Open file desriptors
 
203
    if not opts.use_vc:
 
204
        oldn, newn = args
 
205
    else:
 
206
        if len(args) == 3:
 
207
            filename, oldr, newr = args
 
208
            oldn = "%s:%s" % (oldr, filename)
 
209
            newn = "%s:%s" % (newr, filename)
 
210
        else:
 
211
            filename, oldr = args
 
212
            newr = None
 
213
            oldn = "%s:%s" % (oldr, filename)
 
214
            newn = filename
 
215
 
 
216
        
 
217
    old, new = None, None
 
218
    oldz, newz = None, None
 
219
    if opts.use_bzr:
 
220
        old = _open(["bzr", "cat", "-r" + oldr, filename],
 
221
                    "Unable to retrieve revision %s of %s" % (oldr, filename))
 
222
        if newr != None:
 
223
            new = _open(["bzr", "cat", "-r" + newr, filename],
 
224
                        "Unable to retrieve revision %s of %s" % (newr, filename))
 
225
    elif opts.use_git:
 
226
        old = _open(["git", "show", oldn],
 
227
                    "Unable to retrieve revision %s of %s" % (oldr, filename))
 
228
        if newr != None:
 
229
            new = _open(["git", "show", newn],
 
230
                        "Unable to retrieve revision %s of %s" % (newr, filename))
 
231
    elif opts.use_rcs:
 
232
        old = _open(["co", "-q", "-p", "-r" + oldr, filename],
 
233
                    "Unable to retrieve revision %s of %s" % (oldr, filename))
 
234
        if newr != None:
 
235
            new = _open(["co", "-q", "-p", "-r" + newr, filename],
 
236
                        "Unable to retrieve revision %s of %s" % (newr, filename))
 
237
    if not opts.use_vc:
 
238
        old = _open(oldn, "Unable to open %s" % oldn)
 
239
    if not opts.use_vc or newr == None:
 
240
        new = _open(newn, "Unable to open %s" % newn)
 
241
 
 
242
    if not old or not new:
 
243
        sys.exit(65)
 
244
 
 
245
    # Parse the zones
 
246
    try:
 
247
        oldz = dns.zone.from_file(old, origin = '.', check_origin=False)
 
248
    except dns.exception.DNSException:
 
249
        sys.stderr.write("Incorrect zonefile: %s\n", old)
 
250
        if opts.tracebacks:
 
251
            traceback.print_exc()
 
252
    try:
 
253
        newz = dns.zone.from_file(new, origin = '.', check_origin=False)
 
254
    except dns.exception.DNSException:
 
255
        sys.stderr.write("Incorrect zonefile: %s\n" % new)
 
256
        if opts.tracebacks:
 
257
            traceback.print_exc()
 
258
    if not oldz or not newz:
 
259
        sys.exit(65)
 
260
 
 
261
    changes = diff_zones(oldz, newz, opts.ignore_ttl, opts.ignore_soa)
 
262
    changes.sort()
 
263
 
 
264
    if not changes:
 
265
        sys.exit(0)
 
266
    if opts.html:
 
267
        print format_changes_html(oldn, newn, changes, opts.ignore_ttl)
 
268
    else:
 
269
        print format_changes_plain(oldn, newn, changes, opts.ignore_ttl)
 
270
    sys.exit(1)