]> git.proxmox.com Git - ceph.git/blame - ceph/src/ceph-volume/ceph_volume/util/system.py
update sources to v12.2.3
[ceph.git] / ceph / src / ceph-volume / ceph_volume / util / system.py
CommitLineData
d2e6a577 1import errno
b32b8144 2import logging
d2e6a577
FG
3import os
4import pwd
5import platform
3efd9988 6import tempfile
d2e6a577
FG
7import uuid
8from ceph_volume import process
9from . import as_string
10
b32b8144 11logger = logging.getLogger(__name__)
d2e6a577
FG
12
13# TODO: get these out of here and into a common area for others to consume
14if platform.system() == 'FreeBSD':
15 FREEBSD = True
16 DEFAULT_FS_TYPE = 'zfs'
17 PROCDIR = '/compat/linux/proc'
18 # FreeBSD does not have blockdevices any more
19 BLOCKDIR = '/dev'
20 ROOTGROUP = 'wheel'
21else:
22 FREEBSD = False
23 DEFAULT_FS_TYPE = 'xfs'
24 PROCDIR = '/proc'
25 BLOCKDIR = '/sys/block'
26 ROOTGROUP = 'root'
27
28
29def generate_uuid():
30 return str(uuid.uuid4())
31
32
33def get_ceph_user_ids():
34 """
35 Return the id and gid of the ceph user
36 """
37 try:
38 user = pwd.getpwnam('ceph')
39 except KeyError:
40 # is this even possible?
41 raise RuntimeError('"ceph" user is not available in the current system')
42 return user[2], user[3]
43
44
45def mkdir_p(path, chown=True):
46 """
47 A `mkdir -p` that defaults to chown the path to the ceph user
48 """
49 try:
50 os.mkdir(path)
51 except OSError as e:
52 if e.errno == errno.EEXIST:
53 pass
54 else:
55 raise
56 if chown:
57 uid, gid = get_ceph_user_ids()
58 os.chown(path, uid, gid)
59
60
61def chown(path, recursive=True):
62 """
63 ``chown`` a path to the ceph user (uid and guid fetched at runtime)
64 """
65 uid, gid = get_ceph_user_ids()
66 if os.path.islink(path):
67 path = os.path.realpath(path)
68 if recursive:
69 process.run(['chown', '-R', 'ceph:ceph', path])
70 else:
71 os.chown(path, uid, gid)
72
73
3efd9988 74def is_binary(path):
d2e6a577 75 """
3efd9988
FG
76 Detect if a file path is a binary or not. Will falsely report as binary
77 when utf-16 encoded. In the ceph universe there is no such risk (yet)
78 """
79 with open(path, 'rb') as fp:
80 contents = fp.read(8192)
81 if b'\x00' in contents: # a null byte may signal binary
82 return True
83 return False
84
85
86class tmp_mount(object):
87 """
88 Temporarily mount a device on a temporary directory,
89 and unmount it upon exit
b32b8144
FG
90
91 When ``encrypted`` is set to ``True``, the exit method will call out to
92 close the device so that it doesn't remain open after mounting. It is
93 assumed that it will be open because otherwise it wouldn't be possible to
94 mount in the first place
3efd9988
FG
95 """
96
b32b8144 97 def __init__(self, device, encrypted=False):
3efd9988
FG
98 self.device = device
99 self.path = None
b32b8144 100 self.encrypted = encrypted
3efd9988
FG
101
102 def __enter__(self):
103 self.path = tempfile.mkdtemp()
104 process.run([
3efd9988
FG
105 'mount',
106 '-v',
107 self.device,
108 self.path
109 ])
110 return self.path
111
112 def __exit__(self, exc_type, exc_val, exc_tb):
113 process.run([
3efd9988
FG
114 'umount',
115 '-v',
116 self.path
117 ])
b32b8144
FG
118 if self.encrypted:
119 # avoid a circular import from the encryption module
120 from ceph_volume.util import encryption
121 encryption.dmcrypt_close(self.device)
122
123
124def unmount(path):
125 """
126 Removes mounts at the given path
127 """
128 process.run([
129 'umount',
130 '-v',
131 path,
132 ])
3efd9988
FG
133
134
135def path_is_mounted(path, destination=None):
136 """
137 Check if the given path is mounted
138 """
139 mounts = get_mounts(paths=True)
140 realpath = os.path.realpath(path)
141 mounted_locations = mounts.get(realpath, [])
142
143 if destination:
3efd9988
FG
144 return destination in mounted_locations
145 return mounted_locations != []
146
d2e6a577 147
3efd9988
FG
148def device_is_mounted(dev, destination=None):
149 """
150 Check if the given device is mounted, optionally validating that a
151 destination exists
152 """
b32b8144
FG
153 plain_mounts = get_mounts(devices=True)
154 realpath_mounts = get_mounts(devices=True, realpath=True)
155 realpath_dev = os.path.realpath(dev) if dev.startswith('/') else dev
3efd9988 156 destination = os.path.realpath(destination) if destination else None
b32b8144
FG
157 # plain mounts
158 plain_dev_mounts = plain_mounts.get(dev, [])
159 realpath_dev_mounts = plain_mounts.get(realpath_dev, [])
160 # realpath mounts
161 plain_dev_real_mounts = realpath_mounts.get(dev, [])
162 realpath_dev_real_mounts = realpath_mounts.get(realpath_dev, [])
163
164 mount_locations = [
165 plain_dev_mounts,
166 realpath_dev_mounts,
167 plain_dev_real_mounts,
168 realpath_dev_real_mounts
169 ]
170
171 for mounts in mount_locations:
172 if mounts: # we have a matching mount
173 if destination:
174 if destination in mounts:
175 logger.info(
176 '%s detected as mounted, exists at destination: %s', dev, destination
177 )
178 return True
179 else:
180 logger.info('%s was found as mounted')
181 return True
182 logger.info('%s was not found as mounted')
183 return False
3efd9988
FG
184
185
b32b8144 186def get_mounts(devices=False, paths=False, realpath=False):
3efd9988
FG
187 """
188 Create a mapping of all available system mounts so that other helpers can
189 detect nicely what path or device is mounted
d2e6a577 190
3efd9988
FG
191 It ignores (most of) non existing devices, but since some setups might need
192 some extra device information, it will make an exception for:
d2e6a577 193
3efd9988
FG
194 - tmpfs
195 - devtmpfs
d2e6a577 196
3efd9988
FG
197 If ``devices`` is set to ``True`` the mapping will be a device-to-path(s),
198 if ``paths`` is set to ``True`` then the mapping will be
199 a path-to-device(s)
b32b8144
FG
200
201 :param realpath: Resolve devices to use their realpaths. This is useful for
202 paths like LVM where more than one path can point to the same device
d2e6a577 203 """
3efd9988
FG
204 devices_mounted = {}
205 paths_mounted = {}
206 do_not_skip = ['tmpfs', 'devtmpfs']
207 default_to_devices = devices is False and paths is False
208
209 with open(PROCDIR + '/mounts', 'rb') as mounts:
210 proc_mounts = mounts.readlines()
211
212 for line in proc_mounts:
213 fields = [as_string(f) for f in line.split()]
214 if len(fields) < 3:
215 continue
b32b8144
FG
216 if realpath:
217 device = os.path.realpath(fields[0]) if fields[0].startswith('/') else fields[0]
218 else:
219 device = fields[0]
3efd9988
FG
220 path = os.path.realpath(fields[1])
221 # only care about actual existing devices
222 if not os.path.exists(device) or not device.startswith('/'):
223 if device not in do_not_skip:
d2e6a577 224 continue
3efd9988
FG
225 if device in devices_mounted.keys():
226 devices_mounted[device].append(path)
227 else:
228 devices_mounted[device] = [path]
229 if path in paths_mounted.keys():
230 paths_mounted[path].append(device)
231 else:
232 paths_mounted[path] = [device]
233
234 # Default to returning information for devices if
235 if devices is True or default_to_devices:
236 return devices_mounted
237 else:
238 return paths_mounted