Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / qa / tasks / samba.py
1 """
2 Samba
3 """
4 import contextlib
5 import logging
6 import sys
7 import time
8
9 from teuthology import misc as teuthology
10 from teuthology.orchestra import run
11 from teuthology.orchestra.daemon import DaemonGroup
12
13 log = logging.getLogger(__name__)
14
15
16 def get_sambas(ctx, roles):
17     """
18     Scan for roles that are samba.  Yield the id of the the samba role
19     (samba.0, samba.1...)  and the associated remote site
20
21     :param ctx: Context
22     :param roles: roles for this test (extracted from yaml files)
23     """
24     for role in roles:
25         assert isinstance(role, basestring)
26         PREFIX = 'samba.'
27         assert role.startswith(PREFIX)
28         id_ = role[len(PREFIX):]
29         (remote,) = ctx.cluster.only(role).remotes.iterkeys()
30         yield (id_, remote)
31
32
33 @contextlib.contextmanager
34 def task(ctx, config):
35     """
36     Setup samba smbd with ceph vfs module.  This task assumes the samba
37     package has already been installed via the install task.
38
39     The config is optional and defaults to starting samba on all nodes.
40     If a config is given, it is expected to be a list of
41     samba nodes to start smbd servers on.
42
43     Example that starts smbd on all samba nodes::
44
45         tasks:
46         - install:
47         - install:
48             project: samba
49             extra_packages: ['samba']
50         - ceph:
51         - samba:
52         - interactive:
53
54     Example that starts smbd on just one of the samba nodes and cifs on the other::
55
56         tasks:
57         - samba: [samba.0]
58         - cifs: [samba.1]
59
60     An optional backend can be specified, and requires a path which smbd will
61     use as the backend storage location:
62
63         roles:
64             - [osd.0, osd.1, osd.2, mon.0, mon.1, mon.2, mds.a]
65             - [client.0, samba.0]
66
67         tasks:
68         - ceph:
69         - ceph-fuse: [client.0]
70         - samba:
71             samba.0:
72               cephfuse: "{testdir}/mnt.0"
73
74     This mounts ceph to {testdir}/mnt.0 using fuse, and starts smbd with
75     a UNC of //localhost/cephfuse.  Access through that UNC will be on
76     the ceph fuse mount point.
77
78     If no arguments are specified in the samba
79     role, the default behavior is to enable the ceph UNC //localhost/ceph
80     and use the ceph vfs module as the smbd backend.
81
82     :param ctx: Context
83     :param config: Configuration
84     """
85     log.info("Setting up smbd with ceph vfs...")
86     assert config is None or isinstance(config, list) or isinstance(config, dict), \
87         "task samba got invalid config"
88
89     if config is None:
90         config = dict(('samba.{id}'.format(id=id_), None)
91                   for id_ in teuthology.all_roles_of_type(ctx.cluster, 'samba'))
92     elif isinstance(config, list):
93         config = dict((name, None) for name in config)
94
95     samba_servers = list(get_sambas(ctx=ctx, roles=config.keys()))
96
97     testdir = teuthology.get_testdir(ctx)
98
99     if not hasattr(ctx, 'daemons'):
100         ctx.daemons = DaemonGroup()
101
102     for id_, remote in samba_servers:
103
104         rolestr = "samba.{id_}".format(id_=id_)
105
106         confextras = """vfs objects = ceph
107   ceph:config_file = /etc/ceph/ceph.conf"""
108
109         unc = "ceph"
110         backend = "/"
111
112         if config[rolestr] is not None:
113             # verify that there's just one parameter in role
114             if len(config[rolestr]) != 1:
115                 log.error("samba config for role samba.{id_} must have only one parameter".format(id_=id_))
116                 raise Exception('invalid config')
117             confextras = ""
118             (unc, backendstr) = config[rolestr].items()[0]
119             backend = backendstr.format(testdir=testdir)
120
121         # on first samba role, set ownership and permissions of ceph root
122         # so that samba tests succeed
123         if config[rolestr] is None and id_ == samba_servers[0][0]:
124             remote.run(
125                     args=[
126                         'mkdir', '-p', '/tmp/cmnt', run.Raw('&&'),
127                         'sudo', 'ceph-fuse', '/tmp/cmnt', run.Raw('&&'),
128                         'sudo', 'chown', 'ubuntu:ubuntu', '/tmp/cmnt/', run.Raw('&&'),
129                         'sudo', 'chmod', '1777', '/tmp/cmnt/', run.Raw('&&'),
130                         'sudo', 'umount', '/tmp/cmnt/', run.Raw('&&'),
131                         'rm', '-rf', '/tmp/cmnt',
132                         ],
133                     )
134         else:
135             remote.run(
136                     args=[
137                         'sudo', 'chown', 'ubuntu:ubuntu', backend, run.Raw('&&'),
138                         'sudo', 'chmod', '1777', backend,
139                         ],
140                     )
141
142         teuthology.sudo_write_file(remote, "/usr/local/samba/etc/smb.conf", """
143 [global]
144   workgroup = WORKGROUP
145   netbios name = DOMAIN
146
147 [{unc}]
148   path = {backend}
149   {extras}
150   writeable = yes
151   valid users = ubuntu
152 """.format(extras=confextras, unc=unc, backend=backend))
153
154         # create ubuntu user
155         remote.run(
156             args=[
157                 'sudo', '/usr/local/samba/bin/smbpasswd', '-e', 'ubuntu',
158                 run.Raw('||'),
159                 'printf', run.Raw('"ubuntu\nubuntu\n"'),
160                 run.Raw('|'),
161                 'sudo', '/usr/local/samba/bin/smbpasswd', '-s', '-a', 'ubuntu'
162             ])
163
164         smbd_cmd = [
165                 'sudo',
166                 'daemon-helper',
167                 'term',
168                 'nostdin',
169                 '/usr/local/samba/sbin/smbd',
170                 '-F',
171                 ]
172         ctx.daemons.add_daemon(remote, 'smbd', id_,
173                                args=smbd_cmd,
174                                logger=log.getChild("smbd.{id_}".format(id_=id_)),
175                                stdin=run.PIPE,
176                                wait=False,
177                                )
178
179         # let smbd initialize, probably a better way...
180         seconds_to_sleep = 100
181         log.info('Sleeping for %s  seconds...' % seconds_to_sleep)
182         time.sleep(seconds_to_sleep)
183         log.info('Sleeping stopped...')
184
185     try:
186         yield
187     finally:
188         log.info('Stopping smbd processes...')
189         exc_info = (None, None, None)
190         for d in ctx.daemons.iter_daemons_of_role('smbd'):
191             try:
192                 d.stop()
193             except (run.CommandFailedError,
194                     run.CommandCrashedError,
195                     run.ConnectionLostError):
196                 exc_info = sys.exc_info()
197                 log.exception('Saw exception from %s.%s', d.role, d.id_)
198         if exc_info != (None, None, None):
199             raise exc_info[0], exc_info[1], exc_info[2]
200
201         for id_, remote in samba_servers:
202             remote.run(
203                 args=[
204                     'sudo',
205                     'rm', '-rf',
206                     '/usr/local/samba/etc/smb.conf',
207                     '/usr/local/samba/private/*',
208                     '/usr/local/samba/var/run/',
209                     '/usr/local/samba/var/locks',
210                     '/usr/local/samba/var/lock',
211                     ],
212                 )
213             # make sure daemons are gone
214             try:
215                 remote.run(
216                     args=[
217                         'while',
218                         'sudo', 'killall', '-9', 'smbd',
219                         run.Raw(';'),
220                         'do', 'sleep', '1',
221                         run.Raw(';'),
222                         'done',
223                         ],
224                     )
225
226                 remote.run(
227                     args=[
228                         'sudo',
229                         'lsof',
230                         backend,
231                         ],
232                     check_status=False
233                     )
234                 remote.run(
235                     args=[
236                         'sudo',
237                         'fuser',
238                         '-M',
239                         backend,
240                         ],
241                     check_status=False
242                     )
243             except Exception:
244                 log.exception("Saw exception")
245                 pass