Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / 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             'mount',
98             '-v',
99             self.device,
100             self.path
101         ])
102         return self.path
103
104     def __exit__(self, exc_type, exc_val, exc_tb):
105         process.run([
106             'umount',
107             '-v',
108             self.path
109         ])
110
111
112 def path_is_mounted(path, destination=None):
113     """
114     Check if the given path is mounted
115     """
116     mounts = get_mounts(paths=True)
117     realpath = os.path.realpath(path)
118     mounted_locations = mounts.get(realpath, [])
119
120     if destination:
121         if destination.startswith('/'):
122             destination = os.path.realpath(destination)
123         return destination in mounted_locations
124     return mounted_locations != []
125
126
127 def device_is_mounted(dev, destination=None):
128     """
129     Check if the given device is mounted, optionally validating that a
130     destination exists
131     """
132     mounts = get_mounts(devices=True)
133     realpath = os.path.realpath(dev) if dev.startswith('/') else dev
134     destination = os.path.realpath(destination) if destination else None
135     mounted_locations = mounts.get(realpath, [])
136
137     if destination:
138         return destination in mounted_locations
139     return mounted_locations != []
140
141
142 def get_mounts(devices=False, paths=False):
143     """
144     Create a mapping of all available system mounts so that other helpers can
145     detect nicely what path or device is mounted
146
147     It ignores (most of) non existing devices, but since some setups might need
148     some extra device information, it will make an exception for:
149
150     - tmpfs
151     - devtmpfs
152
153     If ``devices`` is set to ``True`` the mapping will be a device-to-path(s),
154     if ``paths`` is set to ``True`` then the mapping will be
155     a path-to-device(s)
156     """
157     devices_mounted = {}
158     paths_mounted = {}
159     do_not_skip = ['tmpfs', 'devtmpfs']
160     default_to_devices = devices is False and paths is False
161
162     with open(PROCDIR + '/mounts', 'rb') as mounts:
163         proc_mounts = mounts.readlines()
164
165     for line in proc_mounts:
166         fields = [as_string(f) for f in line.split()]
167         if len(fields) < 3:
168             continue
169         device = os.path.realpath(fields[0]) if fields[0].startswith('/') else fields[0]
170         path = os.path.realpath(fields[1])
171         # only care about actual existing devices
172         if not os.path.exists(device) or not device.startswith('/'):
173             if device not in do_not_skip:
174                 continue
175         if device in devices_mounted.keys():
176             devices_mounted[device].append(path)
177         else:
178             devices_mounted[device] = [path]
179         if path in paths_mounted.keys():
180             paths_mounted[path].append(device)
181         else:
182             paths_mounted[path] = [device]
183
184     # Default to returning information for devices if
185     if devices is True or default_to_devices:
186         return devices_mounted
187     else:
188         return paths_mounted