]> git.proxmox.com Git - ceph.git/blob - ceph/src/ceph-volume/ceph_volume/util/system.py
update sources to 12.2.2
[ceph.git] / ceph / src / ceph-volume / ceph_volume / util / system.py
1 import errno
2 import os
3 import pwd
4 import platform
5 import tempfile
6 import uuid
7 from ceph_volume import process
8 from . import as_string
9
10
11 # TODO: get these out of here and into a common area for others to consume
12 if platform.system() == 'FreeBSD':
13 FREEBSD = True
14 DEFAULT_FS_TYPE = 'zfs'
15 PROCDIR = '/compat/linux/proc'
16 # FreeBSD does not have blockdevices any more
17 BLOCKDIR = '/dev'
18 ROOTGROUP = 'wheel'
19 else:
20 FREEBSD = False
21 DEFAULT_FS_TYPE = 'xfs'
22 PROCDIR = '/proc'
23 BLOCKDIR = '/sys/block'
24 ROOTGROUP = 'root'
25
26
27 def generate_uuid():
28 return str(uuid.uuid4())
29
30
31 def get_ceph_user_ids():
32 """
33 Return the id and gid of the ceph user
34 """
35 try:
36 user = pwd.getpwnam('ceph')
37 except KeyError:
38 # is this even possible?
39 raise RuntimeError('"ceph" user is not available in the current system')
40 return user[2], user[3]
41
42
43 def mkdir_p(path, chown=True):
44 """
45 A `mkdir -p` that defaults to chown the path to the ceph user
46 """
47 try:
48 os.mkdir(path)
49 except OSError as e:
50 if e.errno == errno.EEXIST:
51 pass
52 else:
53 raise
54 if chown:
55 uid, gid = get_ceph_user_ids()
56 os.chown(path, uid, gid)
57
58
59 def chown(path, recursive=True):
60 """
61 ``chown`` a path to the ceph user (uid and guid fetched at runtime)
62 """
63 uid, gid = get_ceph_user_ids()
64 if os.path.islink(path):
65 path = os.path.realpath(path)
66 if recursive:
67 process.run(['chown', '-R', 'ceph:ceph', path])
68 else:
69 os.chown(path, uid, gid)
70
71
72 def is_binary(path):
73 """
74 Detect if a file path is a binary or not. Will falsely report as binary
75 when utf-16 encoded. In the ceph universe there is no such risk (yet)
76 """
77 with open(path, 'rb') as fp:
78 contents = fp.read(8192)
79 if b'\x00' in contents: # a null byte may signal binary
80 return True
81 return False
82
83
84 class tmp_mount(object):
85 """
86 Temporarily mount a device on a temporary directory,
87 and unmount it upon exit
88 """
89
90 def __init__(self, device):
91 self.device = device
92 self.path = None
93
94 def __enter__(self):
95 self.path = tempfile.mkdtemp()
96 process.run([
97 'sudo',
98 'mount',
99 '-v',
100 self.device,
101 self.path
102 ])
103 return self.path
104
105 def __exit__(self, exc_type, exc_val, exc_tb):
106 process.run([
107 'sudo',
108 'umount',
109 '-v',
110 self.path
111 ])
112
113
114 def path_is_mounted(path, destination=None):
115 """
116 Check if the given path is mounted
117 """
118 mounts = get_mounts(paths=True)
119 realpath = os.path.realpath(path)
120 mounted_locations = mounts.get(realpath, [])
121
122 if destination:
123 if destination.startswith('/'):
124 destination = os.path.realpath(destination)
125 return destination in mounted_locations
126 return mounted_locations != []
127
128
129 def device_is_mounted(dev, destination=None):
130 """
131 Check if the given device is mounted, optionally validating that a
132 destination exists
133 """
134 mounts = get_mounts(devices=True)
135 realpath = os.path.realpath(dev) if dev.startswith('/') else dev
136 destination = os.path.realpath(destination) if destination else None
137 mounted_locations = mounts.get(realpath, [])
138
139 if destination:
140 return destination in mounted_locations
141 return mounted_locations != []
142
143
144 def get_mounts(devices=False, paths=False):
145 """
146 Create a mapping of all available system mounts so that other helpers can
147 detect nicely what path or device is mounted
148
149 It ignores (most of) non existing devices, but since some setups might need
150 some extra device information, it will make an exception for:
151
152 - tmpfs
153 - devtmpfs
154
155 If ``devices`` is set to ``True`` the mapping will be a device-to-path(s),
156 if ``paths`` is set to ``True`` then the mapping will be
157 a path-to-device(s)
158 """
159 devices_mounted = {}
160 paths_mounted = {}
161 do_not_skip = ['tmpfs', 'devtmpfs']
162 default_to_devices = devices is False and paths is False
163
164 with open(PROCDIR + '/mounts', 'rb') as mounts:
165 proc_mounts = mounts.readlines()
166
167 for line in proc_mounts:
168 fields = [as_string(f) for f in line.split()]
169 if len(fields) < 3:
170 continue
171 device = os.path.realpath(fields[0]) if fields[0].startswith('/') else fields[0]
172 path = os.path.realpath(fields[1])
173 # only care about actual existing devices
174 if not os.path.exists(device) or not device.startswith('/'):
175 if device not in do_not_skip:
176 continue
177 if device in devices_mounted.keys():
178 devices_mounted[device].append(path)
179 else:
180 devices_mounted[device] = [path]
181 if path in paths_mounted.keys():
182 paths_mounted[path].append(device)
183 else:
184 paths_mounted[path] = [device]
185
186 # Default to returning information for devices if
187 if devices is True or default_to_devices:
188 return devices_mounted
189 else:
190 return paths_mounted