summaryrefslogtreecommitdiffstats
path: root/src/ceph/qa/tasks/samba.py
blob: 8272e8b9539c449bd7256a2c0a2bc742094153ce (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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
"""
Samba
"""
import contextlib
import logging
import sys
import time

from teuthology import misc as teuthology
from teuthology.orchestra import run
from teuthology.orchestra.daemon import DaemonGroup

log = logging.getLogger(__name__)


def get_sambas(ctx, roles):
    """
    Scan for roles that are samba.  Yield the id of the the samba role
    (samba.0, samba.1...)  and the associated remote site

    :param ctx: Context
    :param roles: roles for this test (extracted from yaml files)
    """
    for role in roles:
        assert isinstance(role, basestring)
        PREFIX = 'samba.'
        assert role.startswith(PREFIX)
        id_ = role[len(PREFIX):]
        (remote,) = ctx.cluster.only(role).remotes.iterkeys()
        yield (id_, remote)


@contextlib.contextmanager
def task(ctx, config):
    """
    Setup samba smbd with ceph vfs module.  This task assumes the samba
    package has already been installed via the install task.

    The config is optional and defaults to starting samba on all nodes.
    If a config is given, it is expected to be a list of
    samba nodes to start smbd servers on.

    Example that starts smbd on all samba nodes::

        tasks:
        - install:
        - install:
            project: samba
            extra_packages: ['samba']
        - ceph:
        - samba:
        - interactive:

    Example that starts smbd on just one of the samba nodes and cifs on the other::

        tasks:
        - samba: [samba.0]
        - cifs: [samba.1]

    An optional backend can be specified, and requires a path which smbd will
    use as the backend storage location:

        roles:
            - [osd.0, osd.1, osd.2, mon.0, mon.1, mon.2, mds.a]
            - [client.0, samba.0]

        tasks:
        - ceph:
        - ceph-fuse: [client.0]
        - samba:
            samba.0:
              cephfuse: "{testdir}/mnt.0"

    This mounts ceph to {testdir}/mnt.0 using fuse, and starts smbd with
    a UNC of //localhost/cephfuse.  Access through that UNC will be on
    the ceph fuse mount point.

    If no arguments are specified in the samba
    role, the default behavior is to enable the ceph UNC //localhost/ceph
    and use the ceph vfs module as the smbd backend.

    :param ctx: Context
    :param config: Configuration
    """
    log.info("Setting up smbd with ceph vfs...")
    assert config is None or isinstance(config, list) or isinstance(config, dict), \
        "task samba got invalid config"

    if config is None:
        config = dict(('samba.{id}'.format(id=id_), None)
                  for id_ in teuthology.all_roles_of_type(ctx.cluster, 'samba'))
    elif isinstance(config, list):
        config = dict((name, None) for name in config)

    samba_servers = list(get_sambas(ctx=ctx, roles=config.keys()))

    testdir = teuthology.get_testdir(ctx)

    if not hasattr(ctx, 'daemons'):
        ctx.daemons = DaemonGroup()

    for id_, remote in samba_servers:

        rolestr = "samba.{id_}".format(id_=id_)

        confextras = """vfs objects = ceph
  ceph:config_file = /etc/ceph/ceph.conf"""

        unc = "ceph"
        backend = "/"

        if config[rolestr] is not None:
            # verify that there's just one parameter in role
            if len(config[rolestr]) != 1:
                log.error("samba config for role samba.{id_} must have only one parameter".format(id_=id_))
                raise Exception('invalid config')
            confextras = ""
            (unc, backendstr) = config[rolestr].items()[0]
            backend = backendstr.format(testdir=testdir)

        # on first samba role, set ownership and permissions of ceph root
        # so that samba tests succeed
        if config[rolestr] is None and id_ == samba_servers[0][0]:
            remote.run(
                    args=[
                        'mkdir', '-p', '/tmp/cmnt', run.Raw('&&'),
                        'sudo', 'ceph-fuse', '/tmp/cmnt', run.Raw('&&'),
                        'sudo', 'chown', 'ubuntu:ubuntu', '/tmp/cmnt/', run.Raw('&&'),
                        'sudo', 'chmod', '1777', '/tmp/cmnt/', run.Raw('&&'),
                        'sudo', 'umount', '/tmp/cmnt/', run.Raw('&&'),
                        'rm', '-rf', '/tmp/cmnt',
                        ],
                    )
        else:
            remote.run(
                    args=[
                        'sudo', 'chown', 'ubuntu:ubuntu', backend, run.Raw('&&'),
                        'sudo', 'chmod', '1777', backend,
                        ],
                    )

        teuthology.sudo_write_file(remote, "/usr/local/samba/etc/smb.conf", """
[global]
  workgroup = WORKGROUP
  netbios name = DOMAIN

[{unc}]
  path = {backend}
  {extras}
  writeable = yes
  valid users = ubuntu
""".format(extras=confextras, unc=unc, backend=backend))

        # create ubuntu user
        remote.run(
            args=[
                'sudo', '/usr/local/samba/bin/smbpasswd', '-e', 'ubuntu',
                run.Raw('||'),
                'printf', run.Raw('"ubuntu\nubuntu\n"'),
                run.Raw('|'),
                'sudo', '/usr/local/samba/bin/smbpasswd', '-s', '-a', 'ubuntu'
            ])

        smbd_cmd = [
                'sudo',
                'daemon-helper',
                'term',
                'nostdin',
                '/usr/local/samba/sbin/smbd',
                '-F',
                ]
        ctx.daemons.add_daemon(remote, 'smbd', id_,
                               args=smbd_cmd,
                               logger=log.getChild("smbd.{id_}".format(id_=id_)),
                               stdin=run.PIPE,
                               wait=False,
                               )

        # let smbd initialize, probably a better way...
        seconds_to_sleep = 100
        log.info('Sleeping for %s  seconds...' % seconds_to_sleep)
        time.sleep(seconds_to_sleep)
        log.info('Sleeping stopped...')

    try:
        yield
    finally:
        log.info('Stopping smbd processes...')
        exc_info = (None, None, None)
        for d in ctx.daemons.iter_daemons_of_role('smbd'):
            try:
                d.stop()
            except (run.CommandFailedError,
                    run.CommandCrashedError,
                    run.ConnectionLostError):
                exc_info = sys.exc_info()
                log.exception('Saw exception from %s.%s', d.role, d.id_)
        if exc_info != (None, None, None):
            raise exc_info[0], exc_info[1], exc_info[2]

        for id_, remote in samba_servers:
            remote.run(
                args=[
                    'sudo',
                    'rm', '-rf',
                    '/usr/local/samba/etc/smb.conf',
                    '/usr/local/samba/private/*',
                    '/usr/local/samba/var/run/',
                    '/usr/local/samba/var/locks',
                    '/usr/local/samba/var/lock',
                    ],
                )
            # make sure daemons are gone
            try:
                remote.run(
                    args=[
                        'while',
                        'sudo', 'killall', '-9', 'smbd',
                        run.Raw(';'),
                        'do', 'sleep', '1',
                        run.Raw(';'),
                        'done',
                        ],
                    )

                remote.run(
                    args=[
                        'sudo',
                        'lsof',
                        backend,
                        ],
                    check_status=False
                    )
                remote.run(
                    args=[
                        'sudo',
                        'fuser',
                        '-M',
                        backend,
                        ],
                    check_status=False
                    )
            except Exception:
                log.exception("Saw exception")
                pass