"Fossies" - the Fresh Open Source Software Archive 
Member "salt-3002.2/salt/version.py" (18 Nov 2020, 27300 Bytes) of package /linux/misc/salt-3002.2.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 "version.py" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
3002.1_vs_3002.2.
1 """
2 Set up the version of Salt
3 """
4
5 import platform
6 import re
7 import sys
8
9 MAX_SIZE = sys.maxsize
10 VERSION_LIMIT = MAX_SIZE - 200
11
12 # ----- ATTENTION --------------------------------------------------------------------------------------------------->
13 #
14 # ALL major version bumps, new release codenames, MUST be defined in the SaltStackVersion.NAMES dictionary, i.e.:
15 #
16 # class SaltStackVersion(object):
17 #
18 # NAMES = {
19 # 'Hydrogen': (2014, 1), # <- This is the tuple to bump versions
20 # ( ... )
21 # }
22 #
23 #
24 # ONLY UPDATE CODENAMES AFTER BRANCHING
25 #
26 # As an example, The Helium codename must only be properly defined with "(2014, 7)" after Hydrogen, "(2014, 1)", has
27 # been branched out into its own branch.
28 #
29 # ALL OTHER VERSION INFORMATION IS EXTRACTED FROM THE GIT TAGS
30 #
31 # <---- ATTENTION ----------------------------------------------------------------------------------------------------
32
33
34 class SaltStackVersion:
35 """
36 Handle SaltStack versions class.
37
38 Knows how to parse ``git describe`` output, knows about release candidates
39 and also supports version comparison.
40 """
41
42 __slots__ = (
43 "name",
44 "major",
45 "minor",
46 "bugfix",
47 "mbugfix",
48 "pre_type",
49 "pre_num",
50 "noc",
51 "sha",
52 )
53
54 git_sha_regex = r"(?P<sha>g?[a-f0-9]{7,40})"
55
56 git_describe_regex = re.compile(
57 r"(?:[^\d]+)?(?P<major>[\d]{1,4})"
58 r"(?:\.(?P<minor>[\d]{1,2}))?"
59 r"(?:\.(?P<bugfix>[\d]{0,2}))?"
60 r"(?:\.(?P<mbugfix>[\d]{0,2}))?"
61 r"(?:(?P<pre_type>rc|a|b|alpha|beta|nb)(?P<pre_num>[\d]{1}))?"
62 r"(?:(?:.*)-(?P<noc>(?:[\d]+|n/a))-" + git_sha_regex + r")?"
63 )
64 git_sha_regex = r"^" + git_sha_regex
65
66 git_sha_regex = re.compile(git_sha_regex)
67
68 # Salt versions after 0.17.0 will be numbered like:
69 # <4-digit-year>.<month>.<bugfix>
70 #
71 # Since the actual version numbers will only be know on release dates, the
72 # periodic table element names will be what's going to be used to name
73 # versions and to be able to mention them.
74
75 NAMES = {
76 # Let's keep at least 3 version names uncommented counting from the
77 # latest release so we can map deprecation warnings to versions.
78 # pylint: disable=E8203
79 # ----- Please refrain from fixing PEP-8 E203 and E265 ----->
80 # The idea is to keep this readable.
81 # -----------------------------------------------------------
82 "Hydrogen": (2014, 1),
83 "Helium": (2014, 7),
84 "Lithium": (2015, 5),
85 "Beryllium": (2015, 8),
86 "Boron": (2016, 3),
87 "Carbon": (2016, 11),
88 "Nitrogen": (2017, 7),
89 "Oxygen": (2018, 3),
90 "Fluorine": (2019, 2),
91 "Neon": (3000,),
92 "Sodium": (3001,),
93 "Magnesium": (3002,),
94 "Aluminium": (MAX_SIZE - 96, 0),
95 "Silicon": (MAX_SIZE - 95, 0),
96 "Phosphorus": (MAX_SIZE - 94, 0),
97 # pylint: disable=E8265
98 #'Sulfur' : (MAX_SIZE - 93, 0),
99 #'Chlorine' : (MAX_SIZE - 92, 0),
100 #'Argon' : (MAX_SIZE - 91, 0),
101 #'Potassium' : (MAX_SIZE - 90, 0),
102 #'Calcium' : (MAX_SIZE - 89, 0),
103 #'Scandium' : (MAX_SIZE - 88, 0),
104 #'Titanium' : (MAX_SIZE - 87, 0),
105 #'Vanadium' : (MAX_SIZE - 86, 0),
106 #'Chromium' : (MAX_SIZE - 85, 0),
107 #'Manganese' : (MAX_SIZE - 84, 0),
108 #'Iron' : (MAX_SIZE - 83, 0),
109 #'Cobalt' : (MAX_SIZE - 82, 0),
110 #'Nickel' : (MAX_SIZE - 81, 0),
111 #'Copper' : (MAX_SIZE - 80, 0),
112 #'Zinc' : (MAX_SIZE - 79, 0),
113 #'Gallium' : (MAX_SIZE - 78, 0),
114 #'Germanium' : (MAX_SIZE - 77, 0),
115 #'Arsenic' : (MAX_SIZE - 76, 0),
116 #'Selenium' : (MAX_SIZE - 75, 0),
117 #'Bromine' : (MAX_SIZE - 74, 0),
118 #'Krypton' : (MAX_SIZE - 73, 0),
119 #'Rubidium' : (MAX_SIZE - 72, 0),
120 #'Strontium' : (MAX_SIZE - 71, 0),
121 #'Yttrium' : (MAX_SIZE - 70, 0),
122 #'Zirconium' : (MAX_SIZE - 69, 0),
123 #'Niobium' : (MAX_SIZE - 68, 0),
124 #'Molybdenum' : (MAX_SIZE - 67, 0),
125 #'Technetium' : (MAX_SIZE - 66, 0),
126 #'Ruthenium' : (MAX_SIZE - 65, 0),
127 #'Rhodium' : (MAX_SIZE - 64, 0),
128 #'Palladium' : (MAX_SIZE - 63, 0),
129 #'Silver' : (MAX_SIZE - 62, 0),
130 #'Cadmium' : (MAX_SIZE - 61, 0),
131 #'Indium' : (MAX_SIZE - 60, 0),
132 #'Tin' : (MAX_SIZE - 59, 0),
133 #'Antimony' : (MAX_SIZE - 58, 0),
134 #'Tellurium' : (MAX_SIZE - 57, 0),
135 #'Iodine' : (MAX_SIZE - 56, 0),
136 #'Xenon' : (MAX_SIZE - 55, 0),
137 #'Caesium' : (MAX_SIZE - 54, 0),
138 #'Barium' : (MAX_SIZE - 53, 0),
139 #'Lanthanum' : (MAX_SIZE - 52, 0),
140 #'Cerium' : (MAX_SIZE - 51, 0),
141 #'Praseodymium' : (MAX_SIZE - 50, 0),
142 #'Neodymium' : (MAX_SIZE - 49, 0),
143 #'Promethium' : (MAX_SIZE - 48, 0),
144 #'Samarium' : (MAX_SIZE - 47, 0),
145 #'Europium' : (MAX_SIZE - 46, 0),
146 #'Gadolinium' : (MAX_SIZE - 45, 0),
147 #'Terbium' : (MAX_SIZE - 44, 0),
148 #'Dysprosium' : (MAX_SIZE - 43, 0),
149 #'Holmium' : (MAX_SIZE - 42, 0),
150 #'Erbium' : (MAX_SIZE - 41, 0),
151 #'Thulium' : (MAX_SIZE - 40, 0),
152 #'Ytterbium' : (MAX_SIZE - 39, 0),
153 #'Lutetium' : (MAX_SIZE - 38, 0),
154 #'Hafnium' : (MAX_SIZE - 37, 0),
155 #'Tantalum' : (MAX_SIZE - 36, 0),
156 #'Tungsten' : (MAX_SIZE - 35, 0),
157 #'Rhenium' : (MAX_SIZE - 34, 0),
158 #'Osmium' : (MAX_SIZE - 33, 0),
159 #'Iridium' : (MAX_SIZE - 32, 0),
160 #'Platinum' : (MAX_SIZE - 31, 0),
161 #'Gold' : (MAX_SIZE - 30, 0),
162 #'Mercury' : (MAX_SIZE - 29, 0),
163 #'Thallium' : (MAX_SIZE - 28, 0),
164 #'Lead' : (MAX_SIZE - 27, 0),
165 #'Bismuth' : (MAX_SIZE - 26, 0),
166 #'Polonium' : (MAX_SIZE - 25, 0),
167 #'Astatine' : (MAX_SIZE - 24, 0),
168 #'Radon' : (MAX_SIZE - 23, 0),
169 #'Francium' : (MAX_SIZE - 22, 0),
170 #'Radium' : (MAX_SIZE - 21, 0),
171 #'Actinium' : (MAX_SIZE - 20, 0),
172 #'Thorium' : (MAX_SIZE - 19, 0),
173 #'Protactinium' : (MAX_SIZE - 18, 0),
174 #'Uranium' : (MAX_SIZE - 17, 0),
175 #'Neptunium' : (MAX_SIZE - 16, 0),
176 #'Plutonium' : (MAX_SIZE - 15, 0),
177 #'Americium' : (MAX_SIZE - 14, 0),
178 #'Curium' : (MAX_SIZE - 13, 0),
179 #'Berkelium' : (MAX_SIZE - 12, 0),
180 #'Californium' : (MAX_SIZE - 11, 0),
181 #'Einsteinium' : (MAX_SIZE - 10, 0),
182 #'Fermium' : (MAX_SIZE - 9, 0),
183 #'Mendelevium' : (MAX_SIZE - 8, 0),
184 #'Nobelium' : (MAX_SIZE - 7, 0),
185 #'Lawrencium' : (MAX_SIZE - 6, 0),
186 #'Rutherfordium': (MAX_SIZE - 5, 0),
187 #'Dubnium' : (MAX_SIZE - 4, 0),
188 #'Seaborgium' : (MAX_SIZE - 3, 0),
189 #'Bohrium' : (MAX_SIZE - 2, 0),
190 #'Hassium' : (MAX_SIZE - 1, 0),
191 #'Meitnerium' : (MAX_SIZE - 0, 0),
192 # <---- Please refrain from fixing PEP-8 E203 and E265 ------
193 # pylint: enable=E8203,E8265
194 }
195
196 LNAMES = {k.lower(): v for (k, v) in iter(NAMES.items())}
197 VNAMES = {v: k for (k, v) in iter(NAMES.items())}
198 RMATCH = {v[:2]: k for (k, v) in iter(NAMES.items())}
199
200 def __init__(
201 self, # pylint: disable=C0103
202 major,
203 minor=None,
204 bugfix=None,
205 mbugfix=0,
206 pre_type=None,
207 pre_num=None,
208 noc=0,
209 sha=None,
210 ):
211
212 if isinstance(major, str):
213 major = int(major)
214
215 if isinstance(minor, str):
216 if not minor:
217 # Empty string
218 minor = None
219 else:
220 minor = int(minor)
221
222 if bugfix is None and not self.new_version(major=major):
223 bugfix = 0
224 elif isinstance(bugfix, str):
225 if not bugfix:
226 bugfix = None
227 else:
228 bugfix = int(bugfix)
229
230 if mbugfix is None:
231 mbugfix = 0
232 elif isinstance(mbugfix, str):
233 mbugfix = int(mbugfix)
234
235 if pre_type is None:
236 pre_type = ""
237 if pre_num is None:
238 pre_num = 0
239 elif isinstance(pre_num, str):
240 pre_num = int(pre_num)
241
242 if noc is None:
243 noc = 0
244 elif isinstance(noc, str) and noc == "n/a":
245 noc = -1
246 elif isinstance(noc, str):
247 noc = int(noc)
248
249 self.major = major
250 self.minor = minor
251 self.bugfix = bugfix
252 self.mbugfix = mbugfix
253 self.pre_type = pre_type
254 self.pre_num = pre_num
255 self.name = self.VNAMES.get((major, minor), None)
256 if self.new_version(major):
257 self.name = self.VNAMES.get((major,), None)
258 self.noc = noc
259 self.sha = sha
260
261 def new_version(self, major):
262 """
263 determine if using new versioning scheme
264 """
265 return bool(int(major) >= 3000 and int(major) < VERSION_LIMIT)
266
267 @classmethod
268 def parse(cls, version_string):
269 if version_string.lower() in cls.LNAMES:
270 return cls.from_name(version_string)
271 vstr = (
272 version_string.decode()
273 if isinstance(version_string, bytes)
274 else version_string
275 )
276 match = cls.git_describe_regex.match(vstr)
277 if not match:
278 raise ValueError(
279 "Unable to parse version string: '{}'".format(version_string)
280 )
281 return cls(*match.groups())
282
283 @classmethod
284 def from_name(cls, name):
285 if name.lower() not in cls.LNAMES:
286 raise ValueError("Named version '{}' is not known".format(name))
287 return cls(*cls.LNAMES[name.lower()])
288
289 @classmethod
290 def from_last_named_version(cls):
291 return cls.from_name(
292 cls.VNAMES[
293 max(
294 [
295 version_info
296 for version_info in cls.VNAMES
297 if version_info[0] < (VERSION_LIMIT)
298 ]
299 )
300 ]
301 )
302
303 @classmethod
304 def next_release(cls):
305 return cls.from_name(
306 cls.VNAMES[
307 min(
308 [
309 version_info
310 for version_info in cls.VNAMES
311 if version_info > cls.from_last_named_version().info
312 ]
313 )
314 ]
315 )
316
317 @property
318 def sse(self):
319 # Higher than 0.17, lower than first date based
320 return 0 < self.major < 2014
321
322 def min_info(self):
323 info = [self.major]
324 if self.new_version(self.major):
325 if self.minor:
326 info.append(self.minor)
327 else:
328 info.extend([self.minor, self.bugfix, self.mbugfix])
329 return info
330
331 @property
332 def info(self):
333 return tuple(self.min_info())
334
335 @property
336 def pre_info(self):
337 info = self.min_info()
338 info.extend([self.pre_type, self.pre_num])
339 return tuple(info)
340
341 @property
342 def noc_info(self):
343 info = self.min_info()
344 info.extend([self.pre_type, self.pre_num, self.noc])
345 return tuple(info)
346
347 @property
348 def full_info(self):
349 info = self.min_info()
350 info.extend([self.pre_type, self.pre_num, self.noc, self.sha])
351 return tuple(info)
352
353 @property
354 def full_info_all_versions(self):
355 """
356 Return the full info regardless
357 of which versioning scheme we
358 are using.
359 """
360 info = [
361 self.major,
362 self.minor,
363 self.bugfix,
364 self.mbugfix,
365 self.pre_type,
366 self.pre_num,
367 self.noc,
368 self.sha,
369 ]
370 return tuple(info)
371
372 @property
373 def string(self):
374 if self.new_version(self.major):
375 version_string = "{}".format(self.major)
376 if self.minor:
377 version_string = "{}.{}".format(self.major, self.minor)
378 else:
379 version_string = "{}.{}.{}".format(self.major, self.minor, self.bugfix)
380 if self.mbugfix:
381 version_string += ".{}".format(self.mbugfix)
382 if self.pre_type:
383 version_string += "{}{}".format(self.pre_type, self.pre_num)
384 if self.noc and self.sha:
385 noc = self.noc
386 if noc < 0:
387 noc = "n/a"
388 version_string += "-{}-{}".format(noc, self.sha)
389 return version_string
390
391 @property
392 def formatted_version(self):
393 if self.name and self.major > 10000:
394 version_string = self.name
395 if self.sse:
396 version_string += " Enterprise"
397 version_string += " (Unreleased)"
398 return version_string
399 version_string = self.string
400 if self.sse:
401 version_string += " Enterprise"
402 if (self.major, self.minor) in self.RMATCH:
403 version_string += " ({})".format(self.RMATCH[(self.major, self.minor)])
404 return version_string
405
406 @property
407 def pre_index(self):
408 if self.new_version(self.major):
409 pre_type = 2
410 if not isinstance(self.minor, int):
411 pre_type = 1
412 else:
413 pre_type = 4
414 return pre_type
415
416 def __str__(self):
417 return self.string
418
419 def __compare__(self, other, method):
420 if not isinstance(other, SaltStackVersion):
421 if isinstance(other, str):
422 other = SaltStackVersion.parse(other)
423 elif isinstance(other, (list, tuple)):
424 other = SaltStackVersion(*other)
425 else:
426 raise ValueError(
427 "Cannot instantiate Version from type '{}'".format(type(other))
428 )
429
430 pre_type = self.pre_index
431 other_pre_type = other.pre_index
432 other_noc_info = list(other.noc_info)
433 noc_info = list(self.noc_info)
434
435 if self.new_version(self.major):
436 if self.minor and not other.minor:
437 # We have minor information, the other side does not
438 if self.minor > 0:
439 other_noc_info[1] = 0
440
441 if not self.minor and other.minor:
442 # The other side has minor information, we don't
443 if other.minor > 0:
444 noc_info[1] = 0
445
446 if self.pre_type and not other.pre_type:
447 # We have pre-release information, the other side doesn't
448 other_noc_info[other_pre_type] = "zzzzz"
449
450 if not self.pre_type and other.pre_type:
451 # The other side has pre-release information, we don't
452 noc_info[pre_type] = "zzzzz"
453
454 return method(tuple(noc_info), tuple(other_noc_info))
455
456 def __lt__(self, other):
457 return self.__compare__(other, lambda _self, _other: _self < _other)
458
459 def __le__(self, other):
460 return self.__compare__(other, lambda _self, _other: _self <= _other)
461
462 def __eq__(self, other):
463 return self.__compare__(other, lambda _self, _other: _self == _other)
464
465 def __ne__(self, other):
466 return self.__compare__(other, lambda _self, _other: _self != _other)
467
468 def __ge__(self, other):
469 return self.__compare__(other, lambda _self, _other: _self >= _other)
470
471 def __gt__(self, other):
472 return self.__compare__(other, lambda _self, _other: _self > _other)
473
474 def __repr__(self):
475 parts = []
476 if self.name:
477 parts.append("name='{}'".format(self.name))
478 parts.extend(["major={}".format(self.major), "minor={}".format(self.minor)])
479
480 if self.new_version(self.major):
481 if not self.minor:
482 parts.remove("".join([x for x in parts if re.search("^minor*", x)]))
483 else:
484 parts.extend(["bugfix={}".format(self.bugfix)])
485
486 if self.mbugfix:
487 parts.append("minor-bugfix={}".format(self.mbugfix))
488 if self.pre_type:
489 parts.append("{}={}".format(self.pre_type, self.pre_num))
490 noc = self.noc
491 if noc == -1:
492 noc = "n/a"
493 if noc and self.sha:
494 parts.extend(["noc={}".format(noc), "sha={}".format(self.sha)])
495 return "<{} {}>".format(self.__class__.__name__, " ".join(parts))
496
497
498 # ----- Hardcoded Salt Codename Version Information ----------------------------------------------------------------->
499 #
500 # There's no need to do anything here. The last released codename will be picked up
501 # --------------------------------------------------------------------------------------------------------------------
502 __saltstack_version__ = SaltStackVersion.from_last_named_version()
503 # <---- Hardcoded Salt Version Information ---------------------------------------------------------------------------
504
505
506 # ----- Dynamic/Runtime Salt Version Information -------------------------------------------------------------------->
507 def __discover_version(saltstack_version):
508 # This might be a 'python setup.py develop' installation type. Let's
509 # discover the version information at runtime.
510 import os
511 import subprocess
512
513 if "SETUP_DIRNAME" in globals():
514 # This is from the exec() call in Salt's setup.py
515 cwd = SETUP_DIRNAME # pylint: disable=E0602
516 if not os.path.exists(os.path.join(cwd, ".git")):
517 # This is not a Salt git checkout!!! Don't even try to parse...
518 return saltstack_version
519 else:
520 cwd = os.path.abspath(os.path.dirname(__file__))
521 if not os.path.exists(os.path.join(os.path.dirname(cwd), ".git")):
522 # This is not a Salt git checkout!!! Don't even try to parse...
523 return saltstack_version
524
525 try:
526 kwargs = dict(stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd)
527
528 if not sys.platform.startswith("win"):
529 # Let's not import `salt.utils` for the above check
530 kwargs["close_fds"] = True
531
532 process = subprocess.Popen(
533 [
534 "git",
535 "describe",
536 "--tags",
537 "--first-parent",
538 "--match",
539 "v[0-9]*",
540 "--always",
541 ],
542 **kwargs
543 )
544
545 out, err = process.communicate()
546
547 if process.returncode != 0:
548 # The git version running this might not support --first-parent
549 # Revert to old command
550 process = subprocess.Popen(
551 ["git", "describe", "--tags", "--match", "v[0-9]*", "--always"],
552 **kwargs
553 )
554 out, err = process.communicate()
555 out = out.decode().strip()
556 err = err.decode().strip()
557
558 if not out or err:
559 return saltstack_version
560
561 if SaltStackVersion.git_sha_regex.match(out):
562 # We only define the parsed SHA and set NOC as ??? (unknown)
563 saltstack_version.sha = out.strip()
564 saltstack_version.noc = -1
565 return saltstack_version
566
567 return SaltStackVersion.parse(out)
568
569 except OSError as os_err:
570 if os_err.errno != 2:
571 # If the errno is not 2(The system cannot find the file
572 # specified), raise the exception so it can be catch by the
573 # developers
574 raise
575 return saltstack_version
576
577
578 def __get_version(saltstack_version):
579 """
580 If we can get a version provided at installation time or from Git, use
581 that instead, otherwise we carry on.
582 """
583 try:
584 # Try to import the version information provided at install time
585 from salt._version import __saltstack_version__ # pylint: disable=E0611,F0401
586
587 return __saltstack_version__
588 except ImportError:
589 return __discover_version(saltstack_version)
590
591
592 # Get additional version information if available
593 __saltstack_version__ = __get_version(__saltstack_version__)
594 # This function has executed once, we're done with it. Delete it!
595 del __get_version
596 # <---- Dynamic/Runtime Salt Version Information ---------------------------------------------------------------------
597
598
599 # ----- Common version related attributes - NO NEED TO CHANGE ------------------------------------------------------->
600 __version_info__ = __saltstack_version__.info
601 __version__ = __saltstack_version__.string
602 # <---- Common version related attributes - NO NEED TO CHANGE --------------------------------------------------------
603
604
605 def salt_information():
606 """
607 Report version of salt.
608 """
609 yield "Salt", __version__
610
611
612 def dependency_information(include_salt_cloud=False):
613 """
614 Report versions of library dependencies.
615 """
616 libs = [
617 ("Python", None, sys.version.rsplit("\n")[0].strip()),
618 ("Jinja2", "jinja2", "__version__"),
619 ("M2Crypto", "M2Crypto", "version"),
620 ("msgpack", "msgpack", "version"),
621 ("msgpack-pure", "msgpack_pure", "version"),
622 ("pycrypto", "Crypto", "__version__"),
623 ("pycryptodome", "Cryptodome", "version_info"),
624 ("PyYAML", "yaml", "__version__"),
625 ("PyZMQ", "zmq", "__version__"),
626 ("ZMQ", "zmq", "zmq_version"),
627 ("Mako", "mako", "__version__"),
628 ("Tornado", "tornado", "version"),
629 ("timelib", "timelib", "version"),
630 ("dateutil", "dateutil", "__version__"),
631 ("pygit2", "pygit2", "__version__"),
632 ("libgit2", "pygit2", "LIBGIT2_VERSION"),
633 ("smmap", "smmap", "__version__"),
634 ("cffi", "cffi", "__version__"),
635 ("pycparser", "pycparser", "__version__"),
636 ("gitdb", "gitdb", "__version__"),
637 ("gitpython", "git", "__version__"),
638 ("python-gnupg", "gnupg", "__version__"),
639 ("mysql-python", "MySQLdb", "__version__"),
640 ("cherrypy", "cherrypy", "__version__"),
641 ("docker-py", "docker", "__version__"),
642 ]
643
644 if include_salt_cloud:
645 libs.append(("Apache Libcloud", "libcloud", "__version__"),)
646
647 for name, imp, attr in libs:
648 if imp is None:
649 yield name, attr
650 continue
651 try:
652 imp = __import__(imp)
653 version = getattr(imp, attr)
654 if callable(version):
655 version = version()
656 if isinstance(version, (tuple, list)):
657 version = ".".join(map(str, version))
658 yield name, version
659 except Exception: # pylint: disable=broad-except
660 yield name, None
661
662
663 def system_information():
664 """
665 Report system versions.
666 """
667 # Late import so that when getting called from setup.py does not break
668 from distro import linux_distribution
669
670 def system_version():
671 """
672 Return host system version.
673 """
674
675 lin_ver = linux_distribution()
676 mac_ver = platform.mac_ver()
677 win_ver = platform.win32_ver()
678
679 # linux_distribution() will return a
680 # distribution on OS X and Windows.
681 # Check mac_ver and win_ver first,
682 # then lin_ver.
683 if mac_ver[0]:
684 if isinstance(mac_ver[1], (tuple, list)) and "".join(mac_ver[1]):
685 return " ".join([mac_ver[0], ".".join(mac_ver[1]), mac_ver[2]])
686 else:
687 return " ".join([mac_ver[0], mac_ver[2]])
688 elif win_ver[0]:
689 return " ".join(win_ver)
690 elif lin_ver[0]:
691 return " ".join(lin_ver)
692 else:
693 return ""
694
695 if platform.win32_ver()[0]:
696 # Get the version and release info based on the Windows Operating
697 # System Product Name. As long as Microsoft maintains a similar format
698 # this should be future proof
699 import win32api # pylint: disable=3rd-party-module-not-gated
700 import win32con # pylint: disable=3rd-party-module-not-gated
701
702 # Get the product name from the registry
703 hkey = win32con.HKEY_LOCAL_MACHINE
704 key = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"
705 value_name = "ProductName"
706 reg_handle = win32api.RegOpenKey(hkey, key)
707
708 # Returns a tuple of (product_name, value_type)
709 product_name, _ = win32api.RegQueryValueEx(reg_handle, value_name)
710
711 version = "Unknown"
712 release = ""
713 if "Server" in product_name:
714 for item in product_name.split(" "):
715 # If it's all digits, then it's version
716 if re.match(r"\d+", item):
717 version = item
718 # If it starts with R and then numbers, it's the release
719 # ie: R2
720 if re.match(r"^R\d+$", item):
721 release = item
722 release = "{}Server{}".format(version, release)
723 else:
724 for item in product_name.split(" "):
725 # If it's a number, decimal number, Thin or Vista, then it's the
726 # version
727 if re.match(r"^(\d+(\.\d+)?)|Thin|Vista$", item):
728 version = item
729 release = version
730
731 _, ver, service_pack, extra = platform.win32_ver()
732 version = " ".join([release, ver, service_pack, extra])
733 else:
734 version = system_version()
735 release = platform.release()
736
737 system = [
738 ("system", platform.system()),
739 ("dist", " ".join(linux_distribution(full_distribution_name=False))),
740 ("release", release),
741 ("machine", platform.machine()),
742 ("version", version),
743 ("locale", __salt_system_encoding__),
744 ]
745
746 for name, attr in system:
747 yield name, attr
748 continue
749
750
751 def versions_information(include_salt_cloud=False):
752 """
753 Report the versions of dependent software.
754 """
755 salt_info = list(salt_information())
756 lib_info = list(dependency_information(include_salt_cloud))
757 sys_info = list(system_information())
758
759 return {
760 "Salt Version": dict(salt_info),
761 "Dependency Versions": dict(lib_info),
762 "System Versions": dict(sys_info),
763 }
764
765
766 def versions_report(include_salt_cloud=False):
767 """
768 Yield each version properly formatted for console output.
769 """
770 ver_info = versions_information(include_salt_cloud)
771 not_installed = "Not Installed"
772 ns_pad = len(not_installed)
773 lib_pad = max(len(name) for name in ver_info["Dependency Versions"])
774 sys_pad = max(len(name) for name in ver_info["System Versions"])
775 padding = max(lib_pad, sys_pad, ns_pad) + 1
776
777 fmt = "{0:>{pad}}: {1}"
778 info = []
779 for ver_type in ("Salt Version", "Dependency Versions", "System Versions"):
780 info.append("{}:".format(ver_type))
781 # List dependencies in alphabetical, case insensitive order
782 for name in sorted(ver_info[ver_type], key=lambda x: x.lower()):
783 ver = fmt.format(
784 name, ver_info[ver_type][name] or not_installed, pad=padding
785 )
786 info.append(ver)
787 info.append(" ")
788
789 yield from info
790
791
792 if __name__ == "__main__":
793 print(__version__)