mirror of
https://github.com/samba-team/samba.git
synced 2024-12-22 13:34:15 +03:00
6ac4833678
Signed-off-by: Rob van der Linde <rob@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org> [abartlet@samba.org Some python2 style super() calls remain due to being an actual, even if reasonable, behaviour change]
619 lines
28 KiB
Python
619 lines
28 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Tests for samba-tool visualize
|
|
# Copyright (C) Andrew Bartlett 2015, 2018
|
|
#
|
|
# by Douglas Bagnall <douglas.bagnall@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/>.
|
|
#
|
|
"""Tests for samba-tool visualize ntdsconn using the test ldif
|
|
topologies.
|
|
|
|
We don't test samba-tool visualize reps here because repsTo and
|
|
repsFrom are not replicated, and there are no actual remote servers to
|
|
query.
|
|
"""
|
|
import os
|
|
import tempfile
|
|
import re
|
|
from io import StringIO
|
|
from samba.tests.samba_tool.base import SambaToolCmdTest
|
|
from samba.kcc import ldif_import_export
|
|
from samba.graph import COLOUR_SETS
|
|
from samba.param import LoadParm
|
|
|
|
MULTISITE_LDIF = os.path.join(os.environ['SRCDIR_ABS'],
|
|
"testdata/ldif-utils-test-multisite.ldif")
|
|
|
|
# UNCONNECTED_LDIF is a single site, unconnected 5DC database that was
|
|
# created using samba-tool domain join in testenv.
|
|
UNCONNECTED_LDIF = os.path.join(os.environ['SRCDIR_ABS'],
|
|
"testdata/unconnected-intrasite.ldif")
|
|
|
|
DOMAIN = "DC=ad,DC=samba,DC=example,DC=com"
|
|
DN_TEMPLATE = "CN=%s,CN=Servers,CN=%s,CN=Sites,CN=Configuration," + DOMAIN
|
|
|
|
MULTISITE_LDIF_DSAS = [
|
|
("WIN01", "Default-First-Site-Name"),
|
|
("WIN08", "Site-4"),
|
|
("WIN07", "Site-4"),
|
|
("WIN06", "Site-3"),
|
|
("WIN09", "Site-5"),
|
|
("WIN10", "Site-5"),
|
|
("WIN02", "Site-2"),
|
|
("WIN04", "Site-2"),
|
|
("WIN03", "Site-2"),
|
|
("WIN05", "Site-2"),
|
|
]
|
|
|
|
|
|
class StringIOThinksItIsATTY(StringIO):
|
|
"""A StringIO that claims to be a TTY for testing --color=auto,
|
|
by switching the stringIO class attribute."""
|
|
def isatty(self):
|
|
return True
|
|
|
|
|
|
def samdb_from_ldif(ldif, tempdir, lp, dsa=None, tag=''):
|
|
if dsa is None:
|
|
dsa_name = 'default-DSA'
|
|
else:
|
|
dsa_name = dsa[:5]
|
|
dburl = os.path.join(tempdir,
|
|
("ldif-to-sambdb-%s-%s" %
|
|
(tag, dsa_name)))
|
|
samdb = ldif_import_export.ldif_to_samdb(dburl, lp, ldif,
|
|
forced_local_dsa=dsa)
|
|
return (samdb, dburl)
|
|
|
|
|
|
def collapse_space(s, keep_empty_lines=False):
|
|
lines = []
|
|
for line in s.splitlines():
|
|
line = ' '.join(line.strip().split())
|
|
if line or keep_empty_lines:
|
|
lines.append(line)
|
|
return '\n'.join(lines)
|
|
|
|
|
|
class SambaToolVisualizeLdif(SambaToolCmdTest):
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.lp = LoadParm()
|
|
self.samdb, self.dbfile = samdb_from_ldif(MULTISITE_LDIF,
|
|
self.tempdir,
|
|
self.lp)
|
|
self.dburl = 'tdb://' + self.dbfile
|
|
|
|
def tearDown(self):
|
|
self.remove_files(self.dbfile)
|
|
super().tearDown()
|
|
|
|
def remove_files(self, *files):
|
|
for f in files:
|
|
self.assertTrue(f.startswith(self.tempdir))
|
|
os.unlink(f)
|
|
|
|
def test_colour(self):
|
|
"""Ensure the colour output is the same as the monochrome output
|
|
EXCEPT for the colours, of which the monochrome one should
|
|
know nothing."""
|
|
colour_re = re.compile('\033' r'\[[\d;]+m')
|
|
result, monochrome, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', self.dburl,
|
|
'--color=no', '-S')
|
|
self.assertCmdSuccess(result, monochrome, err)
|
|
self.assertFalse(colour_re.findall(monochrome))
|
|
|
|
colour_args = [['--color=yes']]
|
|
colour_args += [['--color-scheme', x] for x in COLOUR_SETS
|
|
if x is not None]
|
|
|
|
for args in colour_args:
|
|
result, out, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', self.dburl,
|
|
'-S', *args)
|
|
self.assertCmdSuccess(result, out, err)
|
|
self.assertTrue(colour_re.search(out),
|
|
f"'{' '.join(args)}' should be colour")
|
|
uncoloured = colour_re.sub('', out)
|
|
|
|
self.assertStringsEqual(monochrome, uncoloured, strip=True)
|
|
|
|
def assert_colour(self, text, has_colour=True, monochrome=None):
|
|
colour_re = re.compile('\033' r'\[[\d;]+m')
|
|
found = colour_re.search(text)
|
|
if has_colour:
|
|
self.assertTrue(found, text)
|
|
else:
|
|
self.assertFalse(found, text)
|
|
if monochrome is not None:
|
|
uncoloured = colour_re.sub('', text)
|
|
self.assertStringsEqual(monochrome, uncoloured, strip=True)
|
|
|
|
def test_colour_auto_tty(self):
|
|
"""Assert the behaviour of --colour=auto with and without
|
|
NO_COLOUR on a fake tty"""
|
|
result, monochrome, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', self.dburl,
|
|
'--color=no', '-S')
|
|
self.assertCmdSuccess(result, monochrome, err)
|
|
self.assert_colour(monochrome, False)
|
|
cls = self.__class__
|
|
|
|
try:
|
|
cls.stringIO = StringIOThinksItIsATTY
|
|
old_no_color = os.environ.pop('NO_COLOR', None)
|
|
# First with no NO_COLOR env var. There should be colour.
|
|
result, out, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', self.dburl,
|
|
'-S',
|
|
'--color=auto')
|
|
self.assertCmdSuccess(result, out, err)
|
|
self.assert_colour(out, True, monochrome)
|
|
|
|
for env, opt, is_colour in [
|
|
# NO_COLOR='' should be as if no NO_COLOR
|
|
['', '--color=auto', True],
|
|
# NO_COLOR='1': we expect no colour
|
|
['1', '--color=auto', False],
|
|
# NO_COLOR='no': we still expect no colour
|
|
['no', '--color=auto', False],
|
|
# NO_COLOR=' ', alias for 'auto'
|
|
[' ', '--color=tty', False],
|
|
# NO_COLOR=' ', alias for 'auto'
|
|
[' ', '--color=if-tty', False],
|
|
# NO_COLOR='', alias for 'auto'
|
|
['', '--color=tty', True],
|
|
# NO_COLOR='', alias for 'no'
|
|
['', '--color=never', False],
|
|
# NO_COLOR='x', alias for 'yes' (--color=yes wins)
|
|
['x', '--color=force', True],
|
|
]:
|
|
os.environ['NO_COLOR'] = env
|
|
|
|
try:
|
|
result, out, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', self.dburl,
|
|
'-S',
|
|
opt)
|
|
except SystemExit:
|
|
# optparse makes us do this
|
|
self.fail(f"optparse rejects {env}, {opt}, {is_colour}")
|
|
|
|
self.assertCmdSuccess(result, out, err)
|
|
self.assert_colour(out, is_colour, monochrome)
|
|
|
|
# with "-o -" output filename alias for stdout.
|
|
result, out, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', self.dburl,
|
|
'-S',
|
|
opt,
|
|
'-o', '-')
|
|
self.assertCmdSuccess(result, out, err)
|
|
self.assert_colour(out, is_colour, monochrome)
|
|
|
|
finally:
|
|
cls.stringIO = StringIO
|
|
if old_no_color is None:
|
|
os.environ.pop('NO_COLOR', None)
|
|
else:
|
|
os.environ['NO_COLOR'] = old_no_color
|
|
|
|
def test_import_ldif_xdot(self):
|
|
"""We can't test actual xdot, but using the environment we can
|
|
persuade samba-tool that a script we write is xdot and ensure
|
|
it gets the right text.
|
|
"""
|
|
result, expected, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', self.dburl,
|
|
'--color=no', '-S',
|
|
'--dot')
|
|
self.assertCmdSuccess(result, expected, err)
|
|
|
|
# not that we're expecting anything here
|
|
old_xdot_path = os.environ.get('SAMBA_TOOL_XDOT_PATH')
|
|
|
|
tmpdir = tempfile.mkdtemp()
|
|
fake_xdot = os.path.join(tmpdir, 'fake_xdot')
|
|
content = os.path.join(tmpdir, 'content')
|
|
f = open(fake_xdot, 'w')
|
|
print('#!/bin/sh', file=f)
|
|
print('cp $1 %s' % content, file=f)
|
|
f.close()
|
|
os.chmod(fake_xdot, 0o700)
|
|
|
|
os.environ['SAMBA_TOOL_XDOT_PATH'] = fake_xdot
|
|
result, empty, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'--importldif', MULTISITE_LDIF,
|
|
'--color=no', '-S',
|
|
'--xdot')
|
|
|
|
f = open(content)
|
|
xdot = f.read()
|
|
f.close()
|
|
os.remove(fake_xdot)
|
|
os.remove(content)
|
|
os.rmdir(tmpdir)
|
|
|
|
if old_xdot_path is not None:
|
|
os.environ['SAMBA_TOOL_XDOT_PATH'] = old_xdot_path
|
|
else:
|
|
del os.environ['SAMBA_TOOL_XDOT_PATH']
|
|
|
|
self.assertCmdSuccess(result, xdot, err)
|
|
self.assertStringsEqual(expected, xdot, strip=True)
|
|
|
|
def test_import_ldif(self):
|
|
"""Make sure the samba-tool visualize --importldif option gives the
|
|
same output as using the externally generated db from the same
|
|
LDIF."""
|
|
result, s1, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', self.dburl,
|
|
'--color=no', '-S')
|
|
self.assertCmdSuccess(result, s1, err)
|
|
|
|
result, s2, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'--importldif', MULTISITE_LDIF,
|
|
'--color=no', '-S')
|
|
self.assertCmdSuccess(result, s2, err)
|
|
|
|
self.assertStringsEqual(s1, s2)
|
|
|
|
def test_output_file(self):
|
|
"""Check that writing to a file works, with and without
|
|
--color=auto."""
|
|
# NOTE, we can't really test --color=auto works with a TTY.
|
|
colour_re = re.compile('\033' r'\[[\d;]+m')
|
|
result, expected, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', self.dburl,
|
|
'--color=auto', '-S')
|
|
self.assertCmdSuccess(result, expected, err)
|
|
# Not a TTY, so stdout output should be colourless
|
|
self.assertFalse(colour_re.search(expected))
|
|
expected = expected.strip()
|
|
|
|
color_auto_file = os.path.join(self.tempdir, 'color-auto')
|
|
|
|
result, out, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', self.dburl,
|
|
'--color=auto', '-S',
|
|
'-o', color_auto_file)
|
|
self.assertCmdSuccess(result, out, err)
|
|
# We wrote to file, so stdout should be empty
|
|
self.assertEqual(out, '')
|
|
f = open(color_auto_file)
|
|
color_auto = f.read()
|
|
f.close()
|
|
self.assertStringsEqual(color_auto, expected, strip=True)
|
|
self.remove_files(color_auto_file)
|
|
|
|
color_no_file = os.path.join(self.tempdir, 'color-no')
|
|
result, out, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', self.dburl,
|
|
'--color=no', '-S',
|
|
'-o', color_no_file)
|
|
self.assertCmdSuccess(result, out, err)
|
|
self.assertEqual(out, '')
|
|
f = open(color_no_file)
|
|
color_no = f.read()
|
|
f.close()
|
|
self.remove_files(color_no_file)
|
|
|
|
self.assertStringsEqual(color_no, expected, strip=True)
|
|
|
|
color_yes_file = os.path.join(self.tempdir, 'color-yes')
|
|
result, out, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', self.dburl,
|
|
'--color=yes', '-S',
|
|
'-o', color_yes_file)
|
|
self.assertCmdSuccess(result, out, err)
|
|
self.assertEqual(out, '')
|
|
f = open(color_yes_file)
|
|
colour_yes = f.read()
|
|
f.close()
|
|
self.assertNotEqual(colour_yes.strip(), expected)
|
|
|
|
self.remove_files(color_yes_file)
|
|
|
|
# Try the magic filename "-", meaning stdout.
|
|
# This doesn't exercise the case when stdout is a TTY
|
|
for c, equal in [('no', True), ('auto', True), ('yes', False)]:
|
|
result, out, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', self.dburl,
|
|
'--color', c,
|
|
'-S', '-o', '-')
|
|
self.assertCmdSuccess(result, out, err)
|
|
self.assertEqual((out.strip() == expected), equal)
|
|
|
|
def test_utf8(self):
|
|
"""Ensure that --utf8 adds at least some expected utf-8, and that it
|
|
isn't there without --utf8."""
|
|
result, utf8, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', self.dburl,
|
|
'--color=no', '-S', '--utf8')
|
|
self.assertCmdSuccess(result, utf8, err)
|
|
|
|
result, ascii, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', self.dburl,
|
|
'--color=no', '-S')
|
|
self.assertCmdSuccess(result, ascii, err)
|
|
for c in ('│', '─', '╭'):
|
|
self.assertTrue(c in utf8, 'UTF8 should contain %s' % c)
|
|
self.assertTrue(c not in ascii, 'ASCII should not contain %s' % c)
|
|
|
|
def test_forced_local_dsa(self):
|
|
# the forced_local_dsa shouldn't make any difference, except
|
|
# for the title line.
|
|
result, target, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', self.dburl,
|
|
'--color=no', '-S')
|
|
self.assertCmdSuccess(result, target, err)
|
|
files = []
|
|
target = target.strip().split('\n', 1)[1]
|
|
for cn, site in MULTISITE_LDIF_DSAS:
|
|
dsa = DN_TEMPLATE % (cn, site)
|
|
samdb, dbfile = samdb_from_ldif(MULTISITE_LDIF,
|
|
self.tempdir,
|
|
self.lp, dsa,
|
|
tag=cn)
|
|
|
|
result, out, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', 'tdb://' + dbfile,
|
|
'--color=no', '-S')
|
|
self.assertCmdSuccess(result, out, err)
|
|
# Separate out the title line, which will differ in the DN.
|
|
title, body = out.strip().split('\n', 1)
|
|
self.assertStringsEqual(target, body)
|
|
self.assertIn(cn, title)
|
|
files.append(dbfile)
|
|
self.remove_files(*files)
|
|
|
|
def test_short_names(self):
|
|
"""Ensure the colour ones are the same as the monochrome ones EXCEPT
|
|
for the colours, of which the monochrome one should know nothing"""
|
|
result, short, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', self.dburl,
|
|
'--color=no', '-S', '--no-key')
|
|
self.assertCmdSuccess(result, short, err)
|
|
result, long, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', self.dburl,
|
|
'--color=no', '--no-key')
|
|
self.assertCmdSuccess(result, long, err)
|
|
|
|
lines = short.split('\n')
|
|
replacements = []
|
|
key_lines = ['']
|
|
short_without_key = []
|
|
for line in lines:
|
|
m = re.match(r"'(.{1,2})' stands for '(.+)'", line)
|
|
if m:
|
|
a, b = m.groups()
|
|
replacements.append((len(a), a, b))
|
|
key_lines.append(line)
|
|
else:
|
|
short_without_key.append(line)
|
|
|
|
short = '\n'.join(short_without_key)
|
|
# we need to replace longest strings first
|
|
replacements.sort(reverse=True)
|
|
short2long = short
|
|
# we don't want to shorten the DC name in the header line.
|
|
long_header, long2short = long.strip().split('\n', 1)
|
|
for _, a, b in replacements:
|
|
short2long = short2long.replace(a, b)
|
|
long2short = long2short.replace(b, a)
|
|
|
|
long2short = '%s\n%s' % (long_header, long2short)
|
|
|
|
# The white space is going to be all wacky, so lets squish it down
|
|
short2long = collapse_space(short2long)
|
|
long2short = collapse_space(long2short)
|
|
short = collapse_space(short)
|
|
long = collapse_space(long)
|
|
|
|
self.assertStringsEqual(short2long, long, strip=True)
|
|
self.assertStringsEqual(short, long2short, strip=True)
|
|
|
|
def test_disconnected_ldif_with_key(self):
|
|
"""Test that the 'unconnected' ldif shows up and exactly matches the
|
|
expected output."""
|
|
# This is not truly a disconnected graph because the
|
|
# vampre/local/promoted DCs are in there and they have
|
|
# relationships, and SERVER2 and SERVER3 for some reason refer
|
|
# to them.
|
|
|
|
samdb, dbfile = samdb_from_ldif(UNCONNECTED_LDIF,
|
|
self.tempdir,
|
|
self.lp, tag='disconnected')
|
|
dburl = 'tdb://' + dbfile
|
|
result, output, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', dburl,
|
|
'--color=no', '-S')
|
|
self.remove_files(dbfile)
|
|
self.assertCmdSuccess(result, output, err)
|
|
self.assertStringsEqual(output,
|
|
EXPECTED_DISTANCE_GRAPH_WITH_KEY)
|
|
|
|
def test_dot_ntdsconn(self):
|
|
"""Graphviz NTDS Connection output"""
|
|
result, dot, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', self.dburl,
|
|
'--color=no', '-S', '--dot',
|
|
'--no-key')
|
|
self.assertCmdSuccess(result, dot, err)
|
|
self.assertStringsEqual(EXPECTED_DOT_MULTISITE_NO_KEY, dot)
|
|
|
|
def test_dot_ntdsconn_disconnected(self):
|
|
"""Graphviz NTDS Connection output from disconnected graph"""
|
|
samdb, dbfile = samdb_from_ldif(UNCONNECTED_LDIF,
|
|
self.tempdir,
|
|
self.lp, tag='disconnected')
|
|
|
|
result, dot, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', 'tdb://' + dbfile,
|
|
'--color=no', '-S', '--dot',
|
|
'-o', '-')
|
|
self.assertCmdSuccess(result, dot, err)
|
|
self.remove_files(dbfile)
|
|
self.assertStringsEqual(EXPECTED_DOT_NTDSCONN_DISCONNECTED, dot,
|
|
strip=True)
|
|
|
|
def test_dot_ntdsconn_disconnected_to_file(self):
|
|
"""Graphviz NTDS Connection output into a file"""
|
|
samdb, dbfile = samdb_from_ldif(UNCONNECTED_LDIF,
|
|
self.tempdir,
|
|
self.lp, tag='disconnected')
|
|
|
|
dot_file = os.path.join(self.tempdir, 'dotfile')
|
|
|
|
result, dot, err = self.runsubcmd("visualize", "ntdsconn",
|
|
'-H', 'tdb://' + dbfile,
|
|
'--color=no', '-S', '--dot',
|
|
'-o', dot_file)
|
|
self.assertCmdSuccess(result, dot, err)
|
|
f = open(dot_file)
|
|
dot = f.read()
|
|
f.close()
|
|
self.assertStringsEqual(EXPECTED_DOT_NTDSCONN_DISCONNECTED, dot)
|
|
|
|
self.remove_files(dbfile, dot_file)
|
|
|
|
|
|
EXPECTED_DOT_MULTISITE_NO_KEY = r"""/* generated by samba */
|
|
digraph A_samba_tool_production {
|
|
label="NTDS Connections known to CN=WIN01,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ad,DC=samba,DC=example,DC=com";
|
|
fontsize=10;
|
|
|
|
node[fontname=Helvetica; fontsize=10];
|
|
|
|
"CN=NTDS Settings,\nCN=WIN01,\nCN=Servers,\nCN=Default-\nFirst-Site-Name,\n...";
|
|
"CN=NTDS Settings,\nCN=WIN02,\nCN=Servers,\nCN=Site-2,\n...";
|
|
"CN=NTDS Settings,\nCN=WIN03,\nCN=Servers,\nCN=Site-2,\n...";
|
|
"CN=NTDS Settings,\nCN=WIN04,\nCN=Servers,\nCN=Site-2,\n...";
|
|
"CN=NTDS Settings,\nCN=WIN05,\nCN=Servers,\nCN=Site-2,\n...";
|
|
"CN=NTDS Settings,\nCN=WIN06,\nCN=Servers,\nCN=Site-3,\n...";
|
|
"CN=NTDS Settings,\nCN=WIN07,\nCN=Servers,\nCN=Site-4,\n...";
|
|
"CN=NTDS Settings,\nCN=WIN08,\nCN=Servers,\nCN=Site-4,\n...";
|
|
"CN=NTDS Settings,\nCN=WIN09,\nCN=Servers,\nCN=Site-5,\n...";
|
|
"CN=NTDS Settings,\nCN=WIN10,\nCN=Servers,\nCN=Site-5,\n...";
|
|
"CN=NTDS Settings,\nCN=WIN01,\nCN=Servers,\nCN=Default-\nFirst-Site-Name,\n..." -> "CN=NTDS Settings,\nCN=WIN03,\nCN=Servers,\nCN=Site-2,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=WIN01,\nCN=Servers,\nCN=Default-\nFirst-Site-Name,\n..." -> "CN=NTDS Settings,\nCN=WIN06,\nCN=Servers,\nCN=Site-3,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=WIN01,\nCN=Servers,\nCN=Default-\nFirst-Site-Name,\n..." -> "CN=NTDS Settings,\nCN=WIN07,\nCN=Servers,\nCN=Site-4,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=WIN01,\nCN=Servers,\nCN=Default-\nFirst-Site-Name,\n..." -> "CN=NTDS Settings,\nCN=WIN08,\nCN=Servers,\nCN=Site-4,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=WIN01,\nCN=Servers,\nCN=Default-\nFirst-Site-Name,\n..." -> "CN=NTDS Settings,\nCN=WIN10,\nCN=Servers,\nCN=Site-5,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=WIN02,\nCN=Servers,\nCN=Site-2,\n..." -> "CN=NTDS Settings,\nCN=WIN04,\nCN=Servers,\nCN=Site-2,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=WIN02,\nCN=Servers,\nCN=Site-2,\n..." -> "CN=NTDS Settings,\nCN=WIN05,\nCN=Servers,\nCN=Site-2,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=WIN03,\nCN=Servers,\nCN=Site-2,\n..." -> "CN=NTDS Settings,\nCN=WIN04,\nCN=Servers,\nCN=Site-2,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=WIN03,\nCN=Servers,\nCN=Site-2,\n..." -> "CN=NTDS Settings,\nCN=WIN05,\nCN=Servers,\nCN=Site-2,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=WIN04,\nCN=Servers,\nCN=Site-2,\n..." -> "CN=NTDS Settings,\nCN=WIN01,\nCN=Servers,\nCN=Default-\nFirst-Site-Name,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=WIN04,\nCN=Servers,\nCN=Site-2,\n..." -> "CN=NTDS Settings,\nCN=WIN02,\nCN=Servers,\nCN=Site-2,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=WIN04,\nCN=Servers,\nCN=Site-2,\n..." -> "CN=NTDS Settings,\nCN=WIN03,\nCN=Servers,\nCN=Site-2,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=WIN05,\nCN=Servers,\nCN=Site-2,\n..." -> "CN=NTDS Settings,\nCN=WIN02,\nCN=Servers,\nCN=Site-2,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=WIN05,\nCN=Servers,\nCN=Site-2,\n..." -> "CN=NTDS Settings,\nCN=WIN03,\nCN=Servers,\nCN=Site-2,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=WIN07,\nCN=Servers,\nCN=Site-4,\n..." -> "CN=NTDS Settings,\nCN=WIN01,\nCN=Servers,\nCN=Default-\nFirst-Site-Name,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=WIN09,\nCN=Servers,\nCN=Site-5,\n..." -> "CN=NTDS Settings,\nCN=WIN10,\nCN=Servers,\nCN=Site-5,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=WIN10,\nCN=Servers,\nCN=Site-5,\n..." -> "CN=NTDS Settings,\nCN=WIN01,\nCN=Servers,\nCN=Default-\nFirst-Site-Name,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=WIN10,\nCN=Servers,\nCN=Site-5,\n..." -> "CN=NTDS Settings,\nCN=WIN09,\nCN=Servers,\nCN=Site-5,\n..." [color="#000000", ];
|
|
}
|
|
|
|
"""
|
|
|
|
|
|
EXPECTED_DOT_NTDSCONN_DISCONNECTED = r"""/* generated by samba */
|
|
digraph A_samba_tool_production {
|
|
label="NTDS Connections known to CN=LOCALDC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=samba,DC=example,DC=com";
|
|
fontsize=10;
|
|
|
|
node[fontname=Helvetica; fontsize=10];
|
|
|
|
"CN=NTDS Settings,\nCN=CLIENT,\n...";
|
|
"CN=NTDS Settings,\nCN=LOCALDC,\n...";
|
|
"CN=NTDS Settings,\nCN=PROMOTEDVDC,\n...";
|
|
"CN=NTDS Settings,\nCN=SERVER1,\n...";
|
|
"CN=NTDS Settings,\nCN=SERVER2,\n...";
|
|
"CN=NTDS Settings,\nCN=SERVER3,\n...";
|
|
"CN=NTDS Settings,\nCN=SERVER4,\n...";
|
|
"CN=NTDS Settings,\nCN=SERVER5,\n...";
|
|
"CN=NTDS Settings,\nCN=LOCALDC,\n..." -> "CN=NTDS Settings,\nCN=PROMOTEDVDC,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=PROMOTEDVDC,\n..." -> "CN=NTDS Settings,\nCN=LOCALDC,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=SERVER2,\n..." -> "CN=NTDS Settings,\nCN=PROMOTEDVDC,\n..." [color="#000000", ];
|
|
"CN=NTDS Settings,\nCN=SERVER3,\n..." -> "CN=NTDS Settings,\nCN=LOCALDC,\n..." [color="#000000", ];
|
|
subgraph cluster_key {
|
|
label="Key";
|
|
subgraph cluster_key_nodes {
|
|
label="";
|
|
color = "invis";
|
|
|
|
}
|
|
subgraph cluster_key_edges {
|
|
label="";
|
|
color = "invis";
|
|
subgraph cluster_key_0_ {
|
|
key_0_e1[label=src; color="#000000"; group="key_0__g"]
|
|
key_0_e2[label=dest; color="#000000"; group="key_0__g"]
|
|
key_0_e1 -> key_0_e2 [constraint = false; color="#000000"]
|
|
key_0__label[shape=plaintext; style=solid; width=2.000000; label="NTDS Connection\r"]
|
|
}
|
|
{key_0__label}
|
|
}
|
|
|
|
elision0[shape=plaintext; style=solid; label="\“...” means “CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=samba,DC=example,DC=com”\r"]
|
|
|
|
}
|
|
"CN=NTDS Settings,\nCN=CLIENT,\n..." -> key_0__label [style=invis];
|
|
"CN=NTDS Settings,\nCN=LOCALDC,\n..." -> key_0__label [style=invis];
|
|
"CN=NTDS Settings,\nCN=PROMOTEDVDC,\n..." -> key_0__label [style=invis];
|
|
"CN=NTDS Settings,\nCN=SERVER1,\n..." -> key_0__label [style=invis];
|
|
"CN=NTDS Settings,\nCN=SERVER2,\n..." -> key_0__label [style=invis];
|
|
"CN=NTDS Settings,\nCN=SERVER3,\n..." -> key_0__label [style=invis];
|
|
"CN=NTDS Settings,\nCN=SERVER4,\n..." -> key_0__label [style=invis];
|
|
"CN=NTDS Settings,\nCN=SERVER5,\n..." -> key_0__label [style=invis]
|
|
key_0__label -> elision0 [style=invis; weight=9]
|
|
|
|
}
|
|
"""
|
|
|
|
EXPECTED_DISTANCE_GRAPH_WITH_KEY = """
|
|
NTDS Connections known to CN=LOCALDC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=samba,DC=example,DC=com
|
|
|
|
destination
|
|
,-------- *,CN=CLIENT+
|
|
|,------- *,CN=LOCALDC+
|
|
||,------ *,CN=PROMOTEDVDC+
|
|
|||,----- *,CN=SERVER1+
|
|
||||,---- *,CN=SERVER2+
|
|
|||||,--- *,CN=SERVER3+
|
|
||||||,-- *,CN=SERVER4+
|
|
source |||||||,- *,CN=SERVER5+
|
|
*,CN=CLIENT+ 0-------
|
|
*,CN=LOCALDC+ -01-----
|
|
*,CN=PROMOTEDVDC+ -10-----
|
|
*,CN=SERVER1+ ---0----
|
|
*,CN=SERVER2+ -21-0---
|
|
*,CN=SERVER3+ -12--0--
|
|
*,CN=SERVER4+ ------0-
|
|
*,CN=SERVER5+ -------0
|
|
|
|
'*' stands for 'CN=NTDS Settings'
|
|
'+' stands for ',CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=samba,DC=example,DC=com'
|
|
|
|
Data can get from source to destination in the indicated number of steps.
|
|
0 means zero steps (it is the same DC)
|
|
1 means a direct link
|
|
2 means a transitive link involving two steps (i.e. one intermediate DC)
|
|
- means there is no connection, even through other DCs
|
|
|
|
"""
|