[SCM] Samba Shared Repository - branch master updated

Karolin Seeger kseeger at samba.org
Sat Jan 13 21:02:02 UTC 2018


The branch, master has been updated
       via  08651a0 samba_kcc: do not commit new nTDSConnection, if we are rodc
       via  a00312d samba_kcc: simplify NCReplica.set_instantiated_flags()
       via  81484f3 samba_kcc: simplify NCReplica constructor
       via  315f445 samba_kcc: clarify readonly logging, removing now unused function
       via  d3f4429 samba_kcc: remove unused functions
       via  d3c5420 samba_kcc: fix dot_file_dir documentation
       via  a090d7e samba_kcc: remove an unused function
       via  c6294c3 samba-tool visualize for understanding AD DC behaviour
       via  ba2306f samba_kcc: use new graph module for writing dot files
       via  cebad22 python/graph: module for generating ASCII and graphviz visualisations
       via  b4a90a6 samba_kcc: respect kcc.read_only flag on RODC
       via  e579d5b samba_kcc: kcc.debug module defers to samba.colour
       via  a46c4a3 python: module containing ANSI colour sequences
       via  f2762d0 python tests: assert string equality, with diff
       via  3f2762d samba_kcc: documentation fix
       via  6678f33 s4:torture/samba_tool_drs: demote the test dc at the end of test_samba_tool_replicate_local()
      from  4b17d36 WHATSNEW: document some more new options

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 08651a08ac10d472a8b170c2f33496192d7faa66
Author: Andrej Gessel <Andrej.Gessel at janztec.com>
Date:   Mon Nov 13 11:07:43 2017 +0100

    samba_kcc: do not commit new nTDSConnection, if we are rodc
    
    Traceback (most recent call last):
    /usr/local/samba/sbin/samba_kcc:   File "/usr/local/samba/sbin/samba_kcc", line 337, in <module>
    /usr/local/samba/sbin/samba_kcc:     attempt_live_connections=opts.attempt_live_connections)
    /usr/local/samba/sbin/samba_kcc:   File "/usr/local/samba/lib/python2.7/site-packages/samba/kcc/__init__.py", line 2644, in run
    /usr/local/samba/sbin/samba_kcc:     all_connected = self.intersite(ping)
    /usr/local/samba/sbin/samba_kcc:   File "/usr/local/samba/lib/python2.7/site-packages/samba/kcc/__init__.py", line 1883, in intersite
    /usr/local/samba/sbin/samba_kcc:     all_connected = self.create_intersite_connections()
    /usr/local/samba/sbin/samba_kcc:   File "/usr/local/samba/lib/python2.7/site-packages/samba/kcc/__init__.py", line 1817, in create_intersite_connections
    /usr/local/samba/sbin/samba_kcc:     part, True)
    /usr/local/samba/sbin/samba_kcc:   File "/usr/local/samba/lib/python2.7/site-packages/samba/kcc/__init__.py", line 1769, in create_connections
    /usr/local/samba/sbin/samba_kcc:     partial_ok, detect_failed)
    /usr/local/samba/sbin/samba_kcc:   File "/usr/local/samba/lib/python2.7/site-packages/samba/kcc/__init__.py", line 1594, in create_connection
    /usr/local/samba/sbin/samba_kcc:     lbh.commit_connections(self.samdb)
    /usr/local/samba/sbin/samba_kcc:   File "/usr/local/samba/lib/python2.7/site-packages/samba/kcc/kcc_utils.py", line 827, in commit_connections
    /usr/local/samba/sbin/samba_kcc:     connect.commit_added(samdb, ro)
    /usr/local/samba/sbin/samba_kcc:   File "/usr/local/samba/lib/python2.7/site-packages/samba/kcc/kcc_utils.py", line 1123, in commit_added
    /usr/local/samba/sbin/samba_kcc:     (self.dnstr, estr))
    /usr/local/samba/sbin/samba_kcc: samba.kcc.kcc_utils.KCCError: Could not add nTDSConnection for (CN=862f0429-c72c-4a81-ae9a-96820bb2f96d,CN=NTDS Settings,
    CN=BUILDHOST,CN=Servers,CN=Testsite,CN=Sites,CN=Configuration,DC=samdom,DC=com) - (Invalid LDB reply type 1)
    ../source4/dsdb/kcc/kcc_periodic.c:693: Failed samba_kcc - NT_STATUS_ACCESS_DENIED
    
    Signed-off-by: Andrej Gessel <Andrej.Gessel at janztec.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    
    Autobuild-User(master): Karolin Seeger <kseeger at samba.org>
    Autobuild-Date(master): Sat Jan 13 22:01:49 CET 2018 on sn-devel-144

commit a00312df7d5a9a2394b41111608c4d988ff4e3f2
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Fri Dec 15 15:58:46 2017 +1300

    samba_kcc: simplify NCReplica.set_instantiated_flags()
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 81484f32f4dfe4aeb5624430575fe791a9063246
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Wed Dec 13 17:50:56 2017 +1300

    samba_kcc: simplify NCReplica constructor
    
    There is nothing to be gained from setting the dn and guid separately
    except subtle bugs.
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 315f445a0256b0b63a344286debb6a27053c4d69
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Wed Dec 13 17:35:29 2017 +1300

    samba_kcc: clarify readonly logging, removing now unused function
    
    The unused function was somewhat misnamed.
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit d3f4429cd6e8a58926753651c015e683b92995ae
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Wed Dec 13 16:04:19 2017 +1300

    samba_kcc: remove unused functions
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit d3c542051fb19559c5699001da8d9da6c7e66712
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Thu Nov 30 09:24:05 2017 +1300

    samba_kcc: fix dot_file_dir documentation
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit a090d7ef52cfd2bbc8bdf7028db0e2237def1f3e
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Thu Nov 16 16:47:32 2017 +1300

    samba_kcc: remove an unused function
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit c6294c3c7b6c97f15daad7d463bda267726245c7
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Thu Aug 10 11:57:24 2017 +1200

    samba-tool visualize for understanding AD DC behaviour
    
    To work out what is happening in a replication graph, it is sometimes
    helpful to use visualisations. We introduce a samba-tool subcommand to
    write Graphviz dot output and generate text-based heatmaps of the
    distance in hops between DCs.
    
    There are two subcommands, two graphical modes, and (roughly) two modes of
    operation with respect to the location of authority.
    
    `samba-tool visualize ntdsconn` looks at NTDS Connections.
    `samba-tool visualize reps` looks at repsTo and repsFrom objects.
    
    In '--distance' mode (default), the distances between DCs are shown in
    a matrix in the terminal. With '--color=yes', this is depicted as a
    heatmap. With '--utf8' it is a lttle prettier.
    
    In '--dot' mode, Graphviz dot output is generated. When viewed using
    dot or xdot, this shows the network as a graph with DCs as vertices
    and connections edges. Certain types of degenerate edges are shown in
    different colours or line-styles.
    
    Normally samba-tool talks to one database; with the '-r' (a.k.a.
    '--talk-to-remote') option attempts are made to contact all the DCs
    known to the first database. This is necessary to get sensible results
    from `samba-tool visualize reps` because the repsFrom/To objects are
    not replicated, and it can reveal replication issues in other modes.
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit ba2306f00d32d2fc55685b388e03e28fd7d97fd7
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Thu Aug 10 15:29:43 2017 +1200

    samba_kcc: use new graph module for writing dot files
    
    We avoid changing the (annoying) signature of write_dot_file().
    
    Using samba_kcc to write dot files may be deprecated.
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit cebad22ce021ce9051fbe664bc699677796e0fb3
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Wed Jan 10 15:25:22 2018 +1300

    python/graph: module for generating ASCII and graphviz visualisations
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit b4a90a650e969cd65b5104d37e9c57275909b336
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Thu Jan 11 21:56:40 2018 +1300

    samba_kcc: respect kcc.read_only flag on RODC
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit e579d5bd48dfc7bc93ecc126d42fd4389ded0e28
Author: Douglas Bagnall <douglas at halo.gen.nz>
Date:   Wed Jan 3 09:20:09 2018 +1300

    samba_kcc: kcc.debug module defers to samba.colour
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit a46c4a39c4d3be88f76c295b0719c025a1c39c3b
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Sun Jan 7 23:17:38 2018 +1300

    python: module containing ANSI colour sequences
    
    This is going to be used by `samba-tool visualize` and samba_kcc.
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit f2762d088001408a706e88e0fe6f46181c01fc3f
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Fri Jan 5 16:45:37 2018 +1300

    python tests: assert string equality, with diff
    
    In the success case this works just like self.assertEqual(),
    but when things fail you get a better representation of where it went
    wrong (a unified diff).
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 3f2762d0b716e8a440cefeb1867caa303e21af40
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Fri Jan 12 07:32:59 2018 +1300

    samba_kcc: documentation fix
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 6678f33274d4f1784635cd11fc63d9d32a9f9b16
Author: Stefan Metzmacher <metze at samba.org>
Date:   Fri Jan 12 14:52:45 2018 +0100

    s4:torture/samba_tool_drs: demote the test dc at the end of test_samba_tool_replicate_local()
    
    Otherwise this taints other tests which might follow.
    
    Signed-off-by: Stefan Metzmacher <metze at samba.org>
    Reviewed-by: Ralph Boehme <slow at samba.org>

-----------------------------------------------------------------------

Summary of changes:
 python/samba/colour.py                         |  50 ++
 python/samba/graph.py                          | 621 +++++++++++++++++++++++++
 python/samba/kcc/__init__.py                   |  21 +-
 python/samba/kcc/debug.py                      |  24 +-
 python/samba/kcc/graph_utils.py                |  37 +-
 python/samba/kcc/kcc_utils.py                  |  39 +-
 python/samba/netcmd/main.py                    |   1 +
 python/samba/netcmd/visualize.py               | 574 +++++++++++++++++++++++
 python/samba/tests/__init__.py                 |  23 +
 python/samba/tests/graph.py                    | 152 ++++++
 python/samba/tests/samba_tool/visualize.py     | 466 +++++++++++++++++++
 python/samba/tests/samba_tool/visualize_drs.py | 110 +++++
 selftest/tests.py                              |   1 +
 source4/selftest/tests.py                      |   6 +-
 source4/torture/drs/python/samba_tool_drs.py   |   3 +
 15 files changed, 2037 insertions(+), 91 deletions(-)
 create mode 100644 python/samba/colour.py
 create mode 100644 python/samba/graph.py
 create mode 100644 python/samba/netcmd/visualize.py
 create mode 100644 python/samba/tests/graph.py
 create mode 100644 python/samba/tests/samba_tool/visualize.py
 create mode 100644 python/samba/tests/samba_tool/visualize_drs.py


Changeset truncated at 500 lines:

diff --git a/python/samba/colour.py b/python/samba/colour.py
new file mode 100644
index 0000000..b3d9a71
--- /dev/null
+++ b/python/samba/colour.py
@@ -0,0 +1,50 @@
+# ANSI codes for 4 bit and xterm-256color
+#
+# Copyright (C) Andrew Bartlett 2018
+#
+# Originally written by Douglas Bagnall
+#
+# 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 3 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/>.
+#
+# The 4 bit colours are available as global variables with names like
+# RED, DARK_RED, REV_RED (for red background), and REV_DARK_RED.
+#
+# The 256-colour codes are obtained using xterm_256_color(n), where n
+# is the number of the desired colour.
+
+# C_NORMAL resets to normal, whatever that is
+C_NORMAL = "\033[0m"
+
+UNDERLINE = "\033[4m"
+
+def _gen_ansi_colours():
+    g = globals()
+    for i, name in enumerate(('BLACK', 'RED', 'GREEN', 'YELLOW', 'BLUE',
+                              'MAGENTA', 'CYAN', 'WHITE')):
+        g[name] = "\033[1;3%dm" % i
+        g['DARK_' + name] = "\033[3%dm" % i
+        g['REV_' + name] = "\033[1;4%dm" % i
+        g['REV_DARK_' + name] = "\033[4%dm" % i
+
+_gen_ansi_colours()
+
+# kcc.debug uses these aliases (which make visual sense)
+PURPLE = DARK_MAGENTA
+GREY = DARK_WHITE
+
+def xterm_256_colour(n, bg=False, bold=False):
+    weight = '01;' if bold else ''
+    target = '48' if bg else '38'
+
+    return "\033[%s%s;5;%dm" % (weight, target, int(n))
diff --git a/python/samba/graph.py b/python/samba/graph.py
new file mode 100644
index 0000000..f626287
--- /dev/null
+++ b/python/samba/graph.py
@@ -0,0 +1,621 @@
+# -*- coding: utf-8 -*-
+# Graph topology utilities and dot file generation
+#
+# Copyright (C) Andrew Bartlett 2018.
+#
+# Written by Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
+#
+# 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 3 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/>.
+
+from __future__ import print_function
+from samba import colour
+import sys
+
+FONT_SIZE = 10
+
+
+def reformat_graph_label(s):
+    """Break DNs over multiple lines, for better shaped and arguably more
+    readable nodes. We try to split after commas, and if necessary
+    after hyphens or failing that in arbitrary places."""
+    if len(s) < 12:
+        return s
+
+    s = s.replace(',', ',\n')
+    pieces = []
+    for p in s.split('\n'):
+        while len(p) > 20:
+            if '-' in p[2:20]:
+                q, p = p.split('-', 1)
+            else:
+                n = len(p) / 12
+                b = len(p) / n
+                q, p = p[:b], p[b:]
+            pieces.append(q + '-')
+        if p:
+            pieces.append(p)
+
+    return '\\n'.join(pieces)
+
+
+def quote_graph_label(s, reformat=False):
+    """Escape a string as graphvis requires."""
+    # escaping inside quotes is simple in dot, because only " is escaped.
+    # there is no need to count backslashes in sequences like \\\\"
+    s = s.replace('"', '\"')
+    if reformat:
+        s = reformat_graph_label(s)
+    return "%s" % s
+
+
+def shorten_vertex_names(edges, vertices, suffix=',...', aggressive=False):
+    """Replace the common suffix (in practice, the base DN) of a number of
+    vertices with a short string (default ",..."). If this seems
+    pointless because the replaced string is very short or the results
+    seem strange, the original vertices are retained.
+
+    :param edges: a sequence of vertex pairs to shorten
+    :param vertices: a sequence of vertices to shorten
+    :param suffix: the replacement string [",..."]
+
+    :return: tuple of (edges, vertices, replacement)
+
+    If no change is made, the returned edges and vertices will be the
+    original lists  and replacement will be None.
+
+    If a change is made, replacement will be a tuple (new, original)
+    indicating the new suffix that replaces the old.
+    """
+    vlist = list(set(x[0] for x in edges) |
+                 set(x[1] for x in edges) |
+                 set(vertices))
+
+    if len(vlist) < 2:
+        return edges, vertices, None
+
+    # walk backwards along all the strings until we meet a character
+    # that is not shared by all.
+    i = -1
+    try:
+        while True:
+            c = set(x[i] for x in vlist)
+            if len(c) > 1:
+                break
+            i -= 1
+    except IndexError:
+        # We have indexed beyond the start of a string, which should
+        # only happen if one node is a strict suffix of all others.
+        return edges, vertices, None
+
+    # add one to get to the last unanimous character.
+    i += 1
+
+    # now, we actually really want to split on a comma. So we walk
+    # back to a comma.
+    x = vlist[0]
+    while i < len(x) and x[i] != ',':
+        i += 1
+
+    if i >= -len(suffix):
+        # there is nothing to gain here
+        return edges, vertices, None
+
+    edges2 = []
+    vertices2 = []
+
+    for a, b in edges:
+        edges2.append((a[:i] + suffix, b[:i] + suffix))
+    for a in vertices:
+        vertices2.append(a[:i] + suffix)
+
+    replacements = [(suffix, a[i:])]
+
+    if aggressive:
+        # Remove known common annoying strings
+        map = dict((v, v) for v in vertices2)
+        for v in vertices2:
+            if ',CN=Servers,' not in v:
+                break
+        else:
+            map = dict((k, v.replace(',CN=Servers,', ',**,'))
+                       for k, v in map.iteritems())
+            replacements.append(('**', 'CN=Servers'))
+
+        for v in vertices2:
+            if not v.startswith('CN=NTDS Settings,'):
+                break
+        else:
+            map = dict((k, v.replace('CN=NTDS Settings,', '*,'))
+                       for k, v in map.iteritems())
+            replacements.append(('*', 'CN=NTDS Settings'))
+
+        edges2 = [(map.get(a, a), map.get(b, b)) for a, b in edges2]
+        vertices2 = [map.get(a, a) for a in vertices2]
+
+    return edges2, vertices2, replacements
+
+
+def compile_graph_key(key_items, nodes_above=[], elisions=None,
+                      prefix='key_', width=2):
+    """Generate a dot file snippet that acts as a legend for a graph.
+
+    :param key_items: sequence of items (is_vertex, style, label)
+    :param nodes_above: list of vertices (pushes key into right position)
+    :param elision: tuple (short, full) indicating suffix replacement
+    :param prefix: string used to generate key node names ["key_"]
+    :param width: default width of node lines
+
+    Each item in key_items is a tuple of (is_vertex, style, label).
+    is_vertex is a boolean indicating whether the item is a vertex
+    (True) or edge (False). Style is a dot style string for the edge
+    or vertex. label is the text associated with the key item.
+    """
+    edge_lines = []
+    edge_names = []
+    vertex_lines = []
+    vertex_names = []
+    order_lines = []
+    for i, item in enumerate(key_items):
+        is_vertex, style, label = item
+        tag = '%s%d_' % (prefix, i)
+        label = quote_graph_label(label)
+        name = '%s_label' % tag
+
+        if is_vertex:
+            order_lines.append(name)
+            vertex_names.append(name)
+            vertex_lines.append('%s[label="%s"; %s]' %
+                                (name, label, style))
+        else:
+            edge_names.append(name)
+            e1 = '%se1' % tag
+            e2 = '%se2' % tag
+            order_lines.append(name)
+            edge_lines.append('subgraph cluster_%s {' % tag)
+            edge_lines.append('%s[label=src; color="#000000"; group="%s_g"]' %
+                              (e1, tag))
+            edge_lines.append('%s[label=dest; color="#000000"; group="%s_g"]' %
+                              (e2, tag))
+            edge_lines.append('%s -> %s [constraint = false; %s]' % (e1, e2,
+                                                                     style))
+            edge_lines.append(('%s[shape=plaintext; style=solid; width=%f; '
+                               'label="%s\\r"]') %
+                              (name, width, label))
+            edge_lines.append('}')
+
+    elision_str = ''
+    if elisions:
+        for i, elision in enumerate(reversed(elisions)):
+            order_lines.append('elision%d' % i)
+            short, long = elision
+            if short[0] == ',' and long[0] == ',':
+                short = short[1:]
+                long = long[1:]
+            elision_str += ('\nelision%d[shape=plaintext; style=solid; '
+                            'label="\ā€œ%sā€  means  ā€œ%sā€\\r"]\n'
+                            % ((i, short, long)))
+
+    above_lines = []
+    if order_lines:
+        for n in nodes_above:
+            above_lines.append('"%s" -> %s [style=invis]' %
+                               (n, order_lines[0]))
+
+    s = ('subgraph cluster_key {\n'
+         'label="Key";\n'
+         'subgraph cluster_key_nodes {\n'
+         'label="";\n'
+         'color = "invis";\n'
+         '%s\n'
+         '}\n'
+         'subgraph cluster_key_edges {\n'
+         'label="";\n'
+         'color = "invis";\n'
+         '%s\n'
+         '{%s}\n'
+         '}\n'
+         '%s\n'
+         '}\n'
+         '%s\n'
+         '%s [style=invis; weight=9]'
+         '\n'
+         % (';\n'.join(vertex_lines),
+            '\n'.join(edge_lines),
+            ' '.join(edge_names),
+            elision_str,
+            ';\n'.join(above_lines),
+            ' -> '.join(order_lines),
+         ))
+
+    return s
+
+
+def dot_graph(vertices, edges,
+              directed=False,
+              title=None,
+              reformat_labels=True,
+              vertex_colors=None,
+              edge_colors=None,
+              edge_labels=None,
+              vertex_styles=None,
+              edge_styles=None,
+              graph_name=None,
+              shorten_names=False,
+              key_items=None,
+              vertex_clusters=None):
+    """Generate a Graphviz representation of a list of vertices and edges.
+
+    :param vertices: list of vertex names (optional).
+    :param edges:    list of (vertex, vertex) pairs
+    :param directed: bool: whether the graph is directed
+    :param title: optional title for the graph
+    :param reformat_labels: whether to wrap long vertex labels
+    :param vertex_colors: if not None, a sequence of colours for the vertices
+    :param edge_colors: if not None, colours for the edges
+    :param edge_labels: if not None, labels for the edges
+    :param vertex_styles: if not None, DOT style strings for vertices
+    :param edge_styles: if not None, DOT style strings for edges
+    :param graph_name: if not None, name of graph
+    :param shorten_names: if True, remove common DN suffixes
+    :param key: (is_vertex, style, description) tuples
+    :param vertex_clusters: list of subgraph cluster names
+
+    Colour, style, and label lists must be the same length as the
+    corresponding list of edges or vertices (or None).
+
+    Colours can be HTML RGB strings ("#FF0000") or common names
+    ("red"), or some other formats you don't want to think about.
+
+    If `vertices` is None, only the vertices mentioned in the edges
+    are shown, and their appearance can be modified using the
+    vertex_colors and vertex_styles arguments. Vertices appearing in
+    the edges but not in the `vertices` list will be shown but their
+    styles can not be modified.
+    """
+    out = []
+    write = out.append
+
+    if vertices is None:
+        vertices = set(x[0] for x in edges) | set(x[1] for x in edges)
+
+    if shorten_names:
+        edges, vertices, elisions = shorten_vertex_names(edges, vertices)
+    else:
+        elisions = None
+
+    if graph_name is None:
+        graph_name = 'A_samba_tool_production'
+
+    if directed:
+        graph_type = 'digraph'
+        connector = '->'
+    else:
+        graph_type = 'graph'
+        connector = '--'
+
+    write('/* generated by samba */')
+    write('%s %s {' % (graph_type, graph_name))
+    if title is not None:
+        write('label="%s";' % (title,))
+    write('fontsize=%s;\n' % (FONT_SIZE))
+    write('node[fontname=Helvetica; fontsize=%s];\n' % (FONT_SIZE))
+
+    prev_cluster = None
+    cluster_n = 0
+    quoted_vertices = []
+    for i, v in enumerate(vertices):
+        v = quote_graph_label(v, reformat_labels)
+        quoted_vertices.append(v)
+        attrs = []
+        if vertex_clusters and vertex_clusters[i]:
+            cluster = vertex_clusters[i]
+            if cluster != prev_cluster:
+                if prev_cluster is not None:
+                    write("}")
+                prev_cluster = cluster
+                n = quote_graph_label(cluster)
+                if cluster:
+                    write('subgraph cluster_%d {' % cluster_n)
+                    cluster_n += 1
+                    write('style = "rounded,dotted";')
+                    write('node [style="filled"; fillcolor=white];')
+                    write('label = "%s";' % n)
+
+        if vertex_styles and vertex_styles[i]:
+            attrs.append(vertex_styles[i])
+        if vertex_colors and vertex_colors[i]:
+            attrs.append('color="%s"' % quote_graph_label(vertex_colors[i]))
+        if attrs:
+            write('"%s" [%s];' % (v, ', '.join(attrs)))
+        else:
+            write('"%s";' % (v,))
+
+    if prev_cluster:
+        write("}")
+
+    for i, edge in enumerate(edges):
+        a, b = edge
+        if a is None:
+            a = "Missing source value"
+        if b is None:
+            b = "Missing destination value"
+
+        a = quote_graph_label(a, reformat_labels)
+        b = quote_graph_label(b, reformat_labels)
+
+        attrs = []
+        if edge_labels:
+            label = quote_graph_label(edge_labels[i])
+            attrs.append('label="%s"' % label)
+        if edge_colors:
+            attrs.append('color="%s"' % quote_graph_label(edge_colors[i]))
+        if edge_styles:
+            attrs.append(edge_styles[i])  # no quoting
+        if attrs:
+            write('"%s" %s "%s" [%s];' % (a, connector, b, ', '.join(attrs)))
+        else:
+            write('"%s" %s "%s";' % (a, connector, b))
+
+    if key_items:
+        key = compile_graph_key(key_items, nodes_above=quoted_vertices,
+                                elisions=elisions)
+        write(key)
+
+    write('}\n')
+    return '\n'.join(out)
+
+
+COLOUR_SETS = {
+    'ansi': {
+        'alternate rows': (colour.DARK_WHITE, colour.BLACK),
+        'disconnected': colour.RED,
+        'connected': colour.GREEN,
+        'transitive': colour.DARK_YELLOW,
+        'header': colour.UNDERLINE,
+        'reset': colour.C_NORMAL,
+    },
+    'ansi-heatmap': {
+        'alternate rows': (colour.DARK_WHITE, colour.BLACK),
+        'disconnected': colour.REV_RED,
+        'connected': colour.REV_GREEN,
+        'transitive': colour.REV_DARK_YELLOW,
+        'header': colour.UNDERLINE,
+        'reset': colour.C_NORMAL,
+    },
+    'xterm-256color': {
+        'alternate rows': (colour.xterm_256_colour(39),
+                           colour.xterm_256_colour(45)),
+        #'alternate rows': (colour.xterm_256_colour(246),
+        #                   colour.xterm_256_colour(247)),
+        'disconnected': colour.xterm_256_colour(124, bg=True),
+        'connected': colour.xterm_256_colour(112),
+        'transitive': colour.xterm_256_colour(214),
+        'transitive scale': (colour.xterm_256_colour(190),
+                             colour.xterm_256_colour(226),
+                             colour.xterm_256_colour(220),
+                             colour.xterm_256_colour(214),
+                             colour.xterm_256_colour(208),
+        ),
+        'header': colour.UNDERLINE,
+        'reset': colour.C_NORMAL,
+    },
+    'xterm-256color-heatmap': {
+        'alternate rows': (colour.xterm_256_colour(171),
+                           colour.xterm_256_colour(207)),
+        #'alternate rows': (colour.xterm_256_colour(246),
+        #                    colour.xterm_256_colour(247)),
+        'disconnected': colour.xterm_256_colour(124, bg=True),
+        'connected': colour.xterm_256_colour(112, bg=True),
+        'transitive': colour.xterm_256_colour(214, bg=True),
+        'transitive scale': (colour.xterm_256_colour(190, bg=True),
+                             colour.xterm_256_colour(226, bg=True),
+                             colour.xterm_256_colour(220, bg=True),
+                             colour.xterm_256_colour(214, bg=True),
+                             colour.xterm_256_colour(208, bg=True),
+        ),
+        'header': colour.UNDERLINE,
+        'reset': colour.C_NORMAL,
+    },
+    None: {
+        'alternate rows': ('',),
+        'disconnected': '',
+        'connected': '',
+        'transitive': '',
+        'header': '',
+        'reset': '',
+    }
+}


-- 
Samba Shared Repository



More information about the samba-cvs mailing list