]> git.proxmox.com Git - ceph.git/blob - ceph/qa/tasks/cephfs/kernel_mount.py
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / qa / tasks / cephfs / kernel_mount.py
1 from StringIO import StringIO
2 import json
3 import logging
4 from textwrap import dedent
5 from teuthology.orchestra.run import CommandFailedError
6 from teuthology import misc
7
8 from teuthology.orchestra import remote as orchestra_remote
9 from teuthology.orchestra import run
10 from teuthology.contextutil import MaxWhileTries
11 from .mount import CephFSMount
12
13 log = logging.getLogger(__name__)
14
15
16 UMOUNT_TIMEOUT = 300
17
18
19 class KernelMount(CephFSMount):
20 def __init__(self, mons, test_dir, client_id, client_remote,
21 ipmi_user, ipmi_password, ipmi_domain):
22 super(KernelMount, self).__init__(test_dir, client_id, client_remote)
23 self.mons = mons
24
25 self.mounted = False
26 self.ipmi_user = ipmi_user
27 self.ipmi_password = ipmi_password
28 self.ipmi_domain = ipmi_domain
29
30 def write_secret_file(self, remote, role, keyring, filename):
31 """
32 Stash the keyring in the filename specified.
33 """
34 remote.run(
35 args=[
36 'adjust-ulimits',
37 'ceph-coverage',
38 '{tdir}/archive/coverage'.format(tdir=self.test_dir),
39 'ceph-authtool',
40 '--name={role}'.format(role=role),
41 '--print-key',
42 keyring,
43 run.Raw('>'),
44 filename,
45 ],
46 )
47
48 def mount(self, mount_path=None, mount_fs_name=None):
49 log.info('Mounting kclient client.{id} at {remote} {mnt}...'.format(
50 id=self.client_id, remote=self.client_remote, mnt=self.mountpoint))
51
52 keyring = self.get_keyring_path()
53 secret = '{tdir}/ceph.data/client.{id}.secret'.format(tdir=self.test_dir, id=self.client_id)
54 self.write_secret_file(self.client_remote, 'client.{id}'.format(id=self.client_id),
55 keyring, secret)
56
57 self.client_remote.run(
58 args=[
59 'mkdir',
60 '--',
61 self.mountpoint,
62 ],
63 )
64
65 if mount_path is None:
66 mount_path = "/"
67
68 opts = 'name={id},secretfile={secret},norequire_active_mds'.format(id=self.client_id,
69 secret=secret)
70
71 if mount_fs_name is not None:
72 opts += ",mds_namespace={0}".format(mount_fs_name)
73
74 self.client_remote.run(
75 args=[
76 'sudo',
77 'adjust-ulimits',
78 'ceph-coverage',
79 '{tdir}/archive/coverage'.format(tdir=self.test_dir),
80 '/sbin/mount.ceph',
81 '{mons}:{mount_path}'.format(mons=','.join(self.mons), mount_path=mount_path),
82 self.mountpoint,
83 '-v',
84 '-o',
85 opts
86 ],
87 )
88
89 self.client_remote.run(
90 args=['sudo', 'chmod', '1777', self.mountpoint])
91
92 self.mounted = True
93
94 def umount(self, force=False):
95 log.debug('Unmounting client client.{id}...'.format(id=self.client_id))
96
97 cmd=['sudo', 'umount', self.mountpoint]
98 if force:
99 cmd.append('-f')
100
101 self.client_remote.run(args=cmd)
102
103 rproc = self.client_remote.run(
104 args=[
105 'rmdir',
106 '--',
107 self.mountpoint,
108 ],
109 wait=False
110 )
111 run.wait([rproc], UMOUNT_TIMEOUT)
112 self.mounted = False
113
114 def cleanup(self):
115 pass
116
117 def umount_wait(self, force=False, require_clean=False):
118 """
119 Unlike the fuse client, the kernel client's umount is immediate
120 """
121 if not self.is_mounted():
122 return
123
124 try:
125 self.umount(force)
126 except (CommandFailedError, MaxWhileTries):
127 if not force:
128 raise
129
130 self.kill()
131 self.kill_cleanup()
132
133 self.mounted = False
134
135 def is_mounted(self):
136 return self.mounted
137
138 def wait_until_mounted(self):
139 """
140 Unlike the fuse client, the kernel client is up and running as soon
141 as the initial mount() function returns.
142 """
143 assert self.mounted
144
145 def teardown(self):
146 super(KernelMount, self).teardown()
147 if self.mounted:
148 self.umount()
149
150 def kill(self):
151 """
152 The Ceph kernel client doesn't have a mechanism to kill itself (doing
153 that in side the kernel would be weird anyway), so we reboot the whole node
154 to get the same effect.
155
156 We use IPMI to reboot, because we don't want the client to send any
157 releases of capabilities.
158 """
159
160 con = orchestra_remote.getRemoteConsole(self.client_remote.hostname,
161 self.ipmi_user,
162 self.ipmi_password,
163 self.ipmi_domain)
164 con.power_off()
165
166 self.mounted = False
167
168 def kill_cleanup(self):
169 assert not self.mounted
170
171 con = orchestra_remote.getRemoteConsole(self.client_remote.hostname,
172 self.ipmi_user,
173 self.ipmi_password,
174 self.ipmi_domain)
175 con.power_on()
176
177 # Wait for node to come back up after reboot
178 misc.reconnect(None, 300, [self.client_remote])
179
180 # Remove mount directory
181 self.client_remote.run(
182 args=[
183 'rmdir',
184 '--',
185 self.mountpoint,
186 ],
187 )
188
189 def _find_debug_dir(self):
190 """
191 Find the debugfs folder for this mount
192 """
193 pyscript = dedent("""
194 import glob
195 import os
196 import json
197
198 def get_id_to_dir():
199 result = {}
200 for dir in glob.glob("/sys/kernel/debug/ceph/*"):
201 mds_sessions_lines = open(os.path.join(dir, "mds_sessions")).readlines()
202 client_id = mds_sessions_lines[1].split()[1].strip('"')
203
204 result[client_id] = dir
205 return result
206
207 print json.dumps(get_id_to_dir())
208 """)
209
210 p = self.client_remote.run(args=[
211 'sudo', 'python', '-c', pyscript
212 ], stdout=StringIO())
213 client_id_to_dir = json.loads(p.stdout.getvalue())
214
215 try:
216 return client_id_to_dir[self.client_id]
217 except KeyError:
218 log.error("Client id '{0}' debug dir not found (clients seen were: {1})".format(
219 self.client_id, ",".join(client_id_to_dir.keys())
220 ))
221 raise
222
223 def _read_debug_file(self, filename):
224 debug_dir = self._find_debug_dir()
225
226 pyscript = dedent("""
227 import os
228
229 print open(os.path.join("{debug_dir}", "{filename}")).read()
230 """).format(debug_dir=debug_dir, filename=filename)
231
232 p = self.client_remote.run(args=[
233 'sudo', 'python', '-c', pyscript
234 ], stdout=StringIO())
235 return p.stdout.getvalue()
236
237 def get_global_id(self):
238 """
239 Look up the CephFS client ID for this mount, using debugfs.
240 """
241
242 assert self.mounted
243
244 mds_sessions = self._read_debug_file("mds_sessions")
245 lines = mds_sessions.split("\n")
246 return int(lines[0].split()[1])
247
248 def get_osd_epoch(self):
249 """
250 Return 2-tuple of osd_epoch, osd_epoch_barrier
251 """
252 osd_map = self._read_debug_file("osdmap")
253 lines = osd_map.split("\n")
254 first_line_tokens = lines[0].split()
255 epoch, barrier = int(first_line_tokens[1]), int(first_line_tokens[3])
256
257 return epoch, barrier