"Fossies" - the Fresh Open Source Software Archive 
Member "salt-3004.1/salt/modules/localemod.py" (1 Mar 2022, 12120 Bytes) of package /linux/misc/salt-3004.1.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style:
standard) with prefixed line numbers.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "localemod.py" see the
Fossies "Dox" file reference documentation and the last
Fossies "Diffs" side-by-side code changes report:
3003.3_vs_3004rc1.
1 """
2 Module for managing locales on POSIX-like systems.
3 """
4
5 import logging
6 import os
7 import re
8
9 import salt.utils.locales
10 import salt.utils.path
11 import salt.utils.platform
12 import salt.utils.systemd
13 from salt.exceptions import CommandExecutionError
14
15 try:
16 import dbus
17 except ImportError:
18 dbus = None
19
20
21 log = logging.getLogger(__name__)
22
23 # Define the module's virtual name
24 __virtualname__ = "locale"
25
26
27 def __virtual__():
28 """
29 Exclude Windows OS.
30 """
31 if salt.utils.platform.is_windows():
32 return False, "Cannot load locale module: windows platforms are unsupported"
33
34 return __virtualname__
35
36
37 def _parse_dbus_locale():
38 """
39 Get the 'System Locale' parameters from dbus
40 """
41 bus = dbus.SystemBus()
42 localed = bus.get_object("org.freedesktop.locale1", "/org/freedesktop/locale1")
43 properties = dbus.Interface(localed, "org.freedesktop.DBus.Properties")
44 system_locale = properties.Get("org.freedesktop.locale1", "Locale")
45
46 ret = {}
47 for env_var in system_locale:
48 env_var = str(env_var)
49 match = re.match(r"^([A-Z_]+)=(.*)$", env_var)
50 if match:
51 ret[match.group(1)] = match.group(2).replace('"', "")
52 else:
53 log.error(
54 'Odd locale parameter "%s" detected in dbus locale '
55 "output. This should not happen. You should "
56 "probably investigate what caused this.",
57 env_var,
58 )
59
60 return ret
61
62
63 def _localectl_status():
64 """
65 Parse localectl status into a dict.
66 :return: dict
67 """
68 if salt.utils.path.which("localectl") is None:
69 raise CommandExecutionError('Unable to find "localectl"')
70
71 ret = {}
72 locale_ctl_out = (__salt__["cmd.run"]("localectl status") or "").strip()
73 ctl_key = None
74 for line in locale_ctl_out.splitlines():
75 if ": " in line: # Keys are separate with ":" and a space (!).
76 ctl_key, ctl_data = line.split(": ")
77 ctl_key = ctl_key.strip().lower().replace(" ", "_")
78 else:
79 ctl_data = line.strip()
80 if not ctl_data:
81 continue
82 if ctl_key:
83 if "=" in ctl_data:
84 loc_set = ctl_data.split("=")
85 if len(loc_set) == 2:
86 if ctl_key not in ret:
87 ret[ctl_key] = {}
88 ret[ctl_key][loc_set[0]] = loc_set[1]
89 else:
90 ret[ctl_key] = {"data": None if ctl_data == "n/a" else ctl_data}
91 if not ret:
92 log.debug(
93 "Unable to find any locale information inside the following data:\n%s",
94 locale_ctl_out,
95 )
96 raise CommandExecutionError('Unable to parse result of "localectl"')
97
98 return ret
99
100
101 def _localectl_set(locale=""):
102 """
103 Use systemd's localectl command to set the LANG locale parameter, making
104 sure not to trample on other params that have been set.
105 """
106 locale_params = (
107 _parse_dbus_locale()
108 if dbus is not None
109 else _localectl_status().get("system_locale", {})
110 )
111 locale_params["LANG"] = str(locale)
112 args = " ".join(
113 ['{}="{}"'.format(k, v) for k, v in locale_params.items() if v is not None]
114 )
115 return not __salt__["cmd.retcode"](
116 "localectl set-locale {}".format(args), python_shell=False
117 )
118
119
120 def list_avail():
121 """
122 Lists available (compiled) locales
123
124 CLI Example:
125
126 .. code-block:: bash
127
128 salt '*' locale.list_avail
129 """
130 return __salt__["cmd.run"]("locale -a").split("\n")
131
132
133 def get_locale():
134 """
135 Get the current system locale
136
137 CLI Example:
138
139 .. code-block:: bash
140
141 salt '*' locale.get_locale
142 """
143 ret = ""
144 lc_ctl = salt.utils.systemd.booted(__context__)
145 # localectl on SLE12 is installed but the integration is still broken in latest SP3 due to
146 # config is rewritten by by many %post installation hooks in the older packages.
147 # If you use it -- you will break your config. This is not the case in SLE15 anymore.
148 if lc_ctl and not (
149 __grains__["os_family"] in ["Suse"] and __grains__["osmajorrelease"] in [12]
150 ):
151 ret = (
152 _parse_dbus_locale()
153 if dbus is not None
154 else _localectl_status()["system_locale"]
155 ).get("LANG", "")
156 else:
157 if "Suse" in __grains__["os_family"]:
158 cmd = 'grep "^RC_LANG" /etc/sysconfig/language'
159 elif "RedHat" in __grains__["os_family"]:
160 cmd = 'grep "^LANG=" /etc/sysconfig/i18n'
161 elif "Debian" in __grains__["os_family"]:
162 # this block only applies to Debian without systemd
163 cmd = 'grep "^LANG=" /etc/default/locale'
164 elif "Gentoo" in __grains__["os_family"]:
165 cmd = "eselect --brief locale show"
166 return __salt__["cmd.run"](cmd).strip()
167 elif "Solaris" in __grains__["os_family"]:
168 cmd = 'grep "^LANG=" /etc/default/init'
169 else: # don't waste time on a failing cmd.run
170 raise CommandExecutionError(
171 'Error: "{}" is unsupported!'.format(__grains__["oscodename"])
172 )
173
174 if cmd:
175 try:
176 ret = __salt__["cmd.run"](cmd).split("=")[1].replace('"', "")
177 except IndexError as err:
178 log.error('Error occurred while running "%s": %s', cmd, err)
179
180 return ret
181
182
183 def set_locale(locale):
184 """
185 Sets the current system locale
186
187 CLI Example:
188
189 .. code-block:: bash
190
191 salt '*' locale.set_locale 'en_US.UTF-8'
192 """
193 lc_ctl = salt.utils.systemd.booted(__context__)
194 # localectl on SLE12 is installed but the integration is broken -- config is rewritten by YaST2
195 if lc_ctl and not (
196 __grains__["os_family"] in ["Suse"] and __grains__["osmajorrelease"] in [12]
197 ):
198 return _localectl_set(locale)
199
200 if "Suse" in __grains__["os_family"]:
201 # this block applies to all SUSE systems - also with systemd
202 if not __salt__["file.file_exists"]("/etc/sysconfig/language"):
203 __salt__["file.touch"]("/etc/sysconfig/language")
204 __salt__["file.replace"](
205 "/etc/sysconfig/language",
206 "^RC_LANG=.*",
207 'RC_LANG="{}"'.format(locale),
208 append_if_not_found=True,
209 )
210 elif "RedHat" in __grains__["os_family"]:
211 if not __salt__["file.file_exists"]("/etc/sysconfig/i18n"):
212 __salt__["file.touch"]("/etc/sysconfig/i18n")
213 __salt__["file.replace"](
214 "/etc/sysconfig/i18n",
215 "^LANG=.*",
216 'LANG="{}"'.format(locale),
217 append_if_not_found=True,
218 )
219 elif "Debian" in __grains__["os_family"]:
220 # this block only applies to Debian without systemd
221 update_locale = salt.utils.path.which("update-locale")
222 if update_locale is None:
223 raise CommandExecutionError(
224 'Cannot set locale: "update-locale" was not found.'
225 )
226 __salt__["cmd.run"](update_locale) # (re)generate /etc/default/locale
227 __salt__["file.replace"](
228 "/etc/default/locale",
229 "^LANG=.*",
230 'LANG="{}"'.format(locale),
231 append_if_not_found=True,
232 )
233 elif "Gentoo" in __grains__["os_family"]:
234 cmd = "eselect --brief locale set {}".format(locale)
235 return __salt__["cmd.retcode"](cmd, python_shell=False) == 0
236 elif "Solaris" in __grains__["os_family"]:
237 if locale not in __salt__["locale.list_avail"]():
238 return False
239 __salt__["file.replace"](
240 "/etc/default/init",
241 "^LANG=.*",
242 'LANG="{}"'.format(locale),
243 append_if_not_found=True,
244 )
245 else:
246 raise CommandExecutionError("Error: Unsupported platform!")
247
248 return True
249
250
251 def avail(locale):
252 """
253 Check if a locale is available.
254
255 .. versionadded:: 2014.7.0
256
257 CLI Example:
258
259 .. code-block:: bash
260
261 salt '*' locale.avail 'en_US.UTF-8'
262 """
263 try:
264 normalized_locale = salt.utils.locales.normalize_locale(locale)
265 except IndexError:
266 log.error('Unable to validate locale "%s"', locale)
267 return False
268 avail_locales = __salt__["locale.list_avail"]()
269 locale_exists = next(
270 (
271 True
272 for x in avail_locales
273 if salt.utils.locales.normalize_locale(x.strip()) == normalized_locale
274 ),
275 False,
276 )
277 return locale_exists
278
279
280 def gen_locale(locale, **kwargs):
281 """
282 Generate a locale. Options:
283
284 .. versionadded:: 2014.7.0
285
286 :param locale: Any locale listed in /usr/share/i18n/locales or
287 /usr/share/i18n/SUPPORTED for Debian and Gentoo based distributions,
288 which require the charmap to be specified as part of the locale
289 when generating it.
290
291 verbose
292 Show extra warnings about errors that are normally ignored.
293
294 CLI Example:
295
296 .. code-block:: bash
297
298 salt '*' locale.gen_locale en_US.UTF-8
299 salt '*' locale.gen_locale 'en_IE.UTF-8 UTF-8' # Debian/Gentoo only
300 """
301 on_debian = __grains__.get("os") == "Debian"
302 on_ubuntu = __grains__.get("os") == "Ubuntu"
303 on_gentoo = __grains__.get("os_family") == "Gentoo"
304 on_suse = __grains__.get("os_family") == "Suse"
305 on_solaris = __grains__.get("os_family") == "Solaris"
306
307 if on_solaris: # all locales are pre-generated
308 return locale in __salt__["locale.list_avail"]()
309
310 locale_info = salt.utils.locales.split_locale(locale)
311 locale_search_str = "{}_{}".format(
312 locale_info["language"], locale_info["territory"]
313 )
314
315 # if the charmap has not been supplied, normalize by appening it
316 if not locale_info["charmap"] and not on_ubuntu:
317 locale_info["charmap"] = locale_info["codeset"]
318 locale = salt.utils.locales.join_locale(locale_info)
319
320 if on_debian or on_gentoo: # file-based search
321 search = "/usr/share/i18n/SUPPORTED"
322 valid = __salt__["file.search"](
323 search, "^{}$".format(locale), flags=re.MULTILINE
324 )
325 else: # directory-based search
326 if on_suse:
327 search = "/usr/share/locale"
328 else:
329 search = "/usr/share/i18n/locales"
330
331 try:
332 valid = locale_search_str in os.listdir(search)
333 except OSError as ex:
334 log.error(ex)
335 raise CommandExecutionError('Locale "{}" is not available.'.format(locale))
336
337 if not valid:
338 log.error('The provided locale "%s" is not found in %s', locale, search)
339 return False
340
341 if os.path.exists("/etc/locale.gen"):
342 __salt__["file.replace"](
343 "/etc/locale.gen",
344 r"^\s*#\s*{}\s*$".format(locale),
345 "{}\n".format(locale),
346 append_if_not_found=True,
347 )
348 elif on_ubuntu:
349 __salt__["file.touch"](
350 "/var/lib/locales/supported.d/{}".format(locale_info["language"])
351 )
352 __salt__["file.replace"](
353 "/var/lib/locales/supported.d/{}".format(locale_info["language"]),
354 locale,
355 locale,
356 append_if_not_found=True,
357 )
358
359 if salt.utils.path.which("locale-gen"):
360 cmd = ["locale-gen"]
361 if on_gentoo:
362 cmd.append("--generate")
363 if on_ubuntu:
364 cmd.append(salt.utils.locales.normalize_locale(locale))
365 else:
366 cmd.append(locale)
367 elif salt.utils.path.which("localedef"):
368 cmd = [
369 "localedef",
370 "--force",
371 "-i",
372 locale_search_str,
373 "-f",
374 locale_info["codeset"],
375 "{}.{}".format(locale_search_str, locale_info["codeset"]),
376 kwargs.get("verbose", False) and "--verbose" or "--quiet",
377 ]
378 else:
379 raise CommandExecutionError(
380 'Command "locale-gen" or "localedef" was not found on this system.'
381 )
382
383 res = __salt__["cmd.run_all"](cmd)
384 if res["retcode"]:
385 log.error(res["stderr"])
386
387 if kwargs.get("verbose"):
388 return res
389 else:
390 return res["retcode"] == 0