summaryrefslogtreecommitdiff
path: root/python/samba/tests/samba_tool/base.py
blob: 58d9b3d6b9f0489c4a12f61ef04a4b551b7a6565 (plain)
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
# Unix SMB/CIFS implementation.
# Copyright (C) Sean Dague <sdague@linux.vnet.ibm.com> 2011
#
# 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/>.

# This provides a wrapper around the cmd interface so that tests can
# easily be built on top of it and have minimal code to run basic tests
# of the commands. A list of the environmental variables can be found in
# ~/selftest/selftest.pl
#
# These can all be accesses via os.environ["VARIBLENAME"] when needed

import random
import string
from samba.auth import system_session
from samba.samdb import SamDB
from samba.compat import StringIO
from samba.netcmd.main import cmd_sambatool
import samba.tests


def truncate_string(s, cutoff=100):
    if len(s) < cutoff + 15:
        return s
    return s[:cutoff] + '[%d more characters]' % (len(s) - cutoff)


class SambaToolCmdTest(samba.tests.BlackboxTestCase):

    def getSamDB(self, *argv):
        """a convenience function to get a samdb instance so that we can query it"""

        # We build a fake command to get the options created the same
        # way the command classes do it. It would be better if the command
        # classes had a way to more cleanly do this, but this lets us write
        # tests for now
        cmd = cmd_sambatool.subcommands["user"].subcommands["setexpiry"]
        parser, optiongroups = cmd._create_parser("user")
        opts, args = parser.parse_args(list(argv))
        # Filter out options from option groups
        args = args[1:]
        kwargs = dict(opts.__dict__)
        for option_group in parser.option_groups:
            for option in option_group.option_list:
                if option.dest is not None:
                    del kwargs[option.dest]
        kwargs.update(optiongroups)

        H = kwargs.get("H", None)
        sambaopts = kwargs.get("sambaopts", None)
        credopts = kwargs.get("credopts", None)

        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp, fallback_machine=True)

        samdb = SamDB(url=H, session_info=system_session(),
                      credentials=creds, lp=lp)
        return samdb

    def runcmd(self, name, *args):
        """run a single level command"""
        cmd = cmd_sambatool.subcommands[name]
        cmd.outf = StringIO()
        cmd.errf = StringIO()
        result = cmd._run("samba-tool %s" % name, *args)
        return (result, cmd.outf.getvalue(), cmd.errf.getvalue())

    def runsubcmd(self, name, sub, *args):
        """run a command with sub commands"""
        # The reason we need this function separate from runcmd is
        # that the .outf StringIO assignment is overridden if we use
        # runcmd, so we can't capture stdout and stderr
        cmd = cmd_sambatool.subcommands[name].subcommands[sub]
        cmd.outf = StringIO()
        cmd.errf = StringIO()
        result = cmd._run("samba-tool %s %s" % (name, sub), *args)
        return (result, cmd.outf.getvalue(), cmd.errf.getvalue())

    def runsublevelcmd(self, name, sublevels, *args):
        """run a command with any number of sub command levels"""
        # Same as runsubcmd, except this handles a varying number of sub-command
        # levels, e.g. 'samba-tool domain passwordsettings pso set', whereas
        # runsubcmd() only handles exactly one level of sub-commands.
        # First, traverse the levels of sub-commands to get the actual cmd
        # object we'll run, and construct the cmd string along the way
        cmd = cmd_sambatool.subcommands[name]
        cmd_str = "samba-tool %s" % name
        for sub in sublevels:
            cmd = cmd.subcommands[sub]
            cmd_str += " %s" % sub
        cmd.outf = StringIO()
        cmd.errf = StringIO()
        result = cmd._run(cmd_str, *args)
        return (result, cmd.outf.getvalue(), cmd.errf.getvalue())

    def assertCmdSuccess(self, exit, out, err, msg=""):
        self.assertIsNone(exit, msg="exit[%s] stdout[%s] stderr[%s]: %s" % (
                          exit, out, err, msg))

    def assertCmdFail(self, val, msg=""):
        self.assertIsNotNone(val, msg)

    def assertMatch(self, base, string, msg=None):
        # Note: we should stop doing this and just use self.assertIn()
        if msg is None:
            msg = "%r is not in %r" % (truncate_string(string),
                                       truncate_string(base))
        self.assertIn(string, base, msg)

    def randomName(self, count=8):
        """Create a random name, cap letters and numbers, and always starting with a letter"""
        name = random.choice(string.ascii_uppercase)
        name += ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for x in range(count - 1))
        return name

    def randomXid(self):
        # pick some hopefully unused, high UID/GID range to avoid interference
        # from the system the test runs on
        xid = random.randint(4711000, 4799000)
        return xid

    def assertWithin(self, val1, val2, delta, msg=""):
        """Assert that val1 is within delta of val2, useful for time computations"""
        self.assertTrue(((val1 + delta) > val2) and ((val1 - delta) < val2), msg)