mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 17:09:03 +00:00
494 lines
18 KiB
Python
494 lines
18 KiB
Python
# -*- coding: utf-8 -*-
|
|
'''
|
|
Set up the version of Salt
|
|
'''
|
|
|
|
# Import python libs
|
|
from __future__ import print_function
|
|
import re
|
|
import sys
|
|
|
|
# Import salt libs
|
|
try:
|
|
from salt._compat import string_types
|
|
except ImportError:
|
|
if sys.version_info[0] == 3:
|
|
string_types = str
|
|
else:
|
|
string_types = basestring
|
|
|
|
# ----- ATTENTION ----------------------------------------------------------->
|
|
#
|
|
# For version bumps, please update `__saltstack_version__` below
|
|
#
|
|
# <---- ATTENTION ------------------------------------------------------------
|
|
|
|
|
|
class SaltStackVersion(object):
|
|
'''
|
|
Handle SaltStack versions class.
|
|
|
|
Knows how to parse ``git describe`` output, knows about release candidates
|
|
and also supports version comparison.
|
|
'''
|
|
|
|
__slots__ = ('name', 'major', 'minor', 'bugfix', 'rc', 'noc', 'sha')
|
|
|
|
git_describe_regex = re.compile(
|
|
r'(?:[^\d]+)?(?P<major>[\d]{1,4})\.(?P<minor>[\d]{1,2})'
|
|
r'(?:\.(?P<bugfix>[\d]{0,2}))?(?:rc(?P<rc>[\d]{1}))?'
|
|
r'(?:(?:.*)-(?P<noc>[\d]+)-(?P<sha>[a-z0-9]{8}))?'
|
|
)
|
|
|
|
# Salt versions after 0.17.0 will be numbered like:
|
|
# <4-digit-year>.<month>.<bugfix>
|
|
#
|
|
# Since the actual version numbers will only be know on release dates, the
|
|
# periodic table element names will be what's going to be used to name
|
|
# versions and to be able to mention them.
|
|
|
|
NAMES = {
|
|
# Let's keep at least 3 version names uncommented counting from the
|
|
# latest release so we can map deprecation warnings to versions.
|
|
|
|
|
|
# ----- Please refrain from fixing PEP-8 E203 ----------------------->
|
|
# The idea is keep this readable
|
|
# --------------------------------------------------------------------
|
|
'Hydrogen': (sys.maxint - 108, 0, 0, 0),
|
|
'Helium': (sys.maxint - 107, 0, 0, 0),
|
|
'Lithium': (sys.maxint - 106, 0, 0, 0),
|
|
'Beryllium': (sys.maxint - 105, 0, 0, 0),
|
|
'Boron': (sys.maxint - 104, 0, 0, 0),
|
|
#'Carbon' : (sys.maxint - 103, 0, 0, 0),
|
|
#'Nitrogen' : (sys.maxint - 102, 0, 0, 0),
|
|
#'Oxygen' : (sys.maxint - 101, 0, 0, 0),
|
|
#'Fluorine' : (sys.maxint - 100, 0, 0, 0),
|
|
#'Neon' : (sys.maxint - 99 , 0, 0, 0),
|
|
#'Sodium' : (sys.maxint - 98 , 0, 0, 0),
|
|
#'Magnesium' : (sys.maxint - 97 , 0, 0, 0),
|
|
#'Aluminium' : (sys.maxint - 96 , 0, 0, 0),
|
|
#'Silicon' : (sys.maxint - 95 , 0, 0, 0),
|
|
#'Phosphorus' : (sys.maxint - 94 , 0, 0, 0),
|
|
#'Sulfur' : (sys.maxint - 93 , 0, 0, 0),
|
|
#'Chlorine' : (sys.maxint - 92 , 0, 0, 0),
|
|
#'Argon' : (sys.maxint - 91 , 0, 0, 0),
|
|
#'Potassium' : (sys.maxint - 90 , 0, 0, 0),
|
|
#'Calcium' : (sys.maxint - 89 , 0, 0, 0),
|
|
#'Scandium' : (sys.maxint - 88 , 0, 0, 0),
|
|
#'Titanium' : (sys.maxint - 87 , 0, 0, 0),
|
|
#'Vanadium' : (sys.maxint - 86 , 0, 0, 0),
|
|
#'Chromium' : (sys.maxint - 85 , 0, 0, 0),
|
|
#'Manganese' : (sys.maxint - 84 , 0, 0, 0),
|
|
#'Iron' : (sys.maxint - 83 , 0, 0, 0),
|
|
#'Cobalt' : (sys.maxint - 82 , 0, 0, 0),
|
|
#'Nickel' : (sys.maxint - 81 , 0, 0, 0),
|
|
#'Copper' : (sys.maxint - 80 , 0, 0, 0),
|
|
#'Zinc' : (sys.maxint - 79 , 0, 0, 0),
|
|
#'Gallium' : (sys.maxint - 78 , 0, 0, 0),
|
|
#'Germanium' : (sys.maxint - 77 , 0, 0, 0),
|
|
#'Arsenic' : (sys.maxint - 76 , 0, 0, 0),
|
|
#'Selenium' : (sys.maxint - 75 , 0, 0, 0),
|
|
#'Bromine' : (sys.maxint - 74 , 0, 0, 0),
|
|
#'Krypton' : (sys.maxint - 73 , 0, 0, 0),
|
|
#'Rubidium' : (sys.maxint - 72 , 0, 0, 0),
|
|
#'Strontium' : (sys.maxint - 71 , 0, 0, 0),
|
|
#'Yttrium' : (sys.maxint - 70 , 0, 0, 0),
|
|
#'Zirconium' : (sys.maxint - 69 , 0, 0, 0),
|
|
#'Niobium' : (sys.maxint - 68 , 0, 0, 0),
|
|
#'Molybdenum' : (sys.maxint - 67 , 0, 0, 0),
|
|
#'Technetium' : (sys.maxint - 66 , 0, 0, 0),
|
|
#'Ruthenium' : (sys.maxint - 65 , 0, 0, 0),
|
|
#'Rhodium' : (sys.maxint - 64 , 0, 0, 0),
|
|
#'Palladium' : (sys.maxint - 63 , 0, 0, 0),
|
|
#'Silver' : (sys.maxint - 62 , 0, 0, 0),
|
|
#'Cadmium' : (sys.maxint - 61 , 0, 0, 0),
|
|
#'Indium' : (sys.maxint - 60 , 0, 0, 0),
|
|
#'Tin' : (sys.maxint - 59 , 0, 0, 0),
|
|
#'Antimony' : (sys.maxint - 58 , 0, 0, 0),
|
|
#'Tellurium' : (sys.maxint - 57 , 0, 0, 0),
|
|
#'Iodine' : (sys.maxint - 56 , 0, 0, 0),
|
|
#'Xenon' : (sys.maxint - 55 , 0, 0, 0),
|
|
#'Caesium' : (sys.maxint - 54 , 0, 0, 0),
|
|
#'Barium' : (sys.maxint - 53 , 0, 0, 0),
|
|
#'Lanthanum' : (sys.maxint - 52 , 0, 0, 0),
|
|
#'Cerium' : (sys.maxint - 51 , 0, 0, 0),
|
|
#'Praseodymium' : (sys.maxint - 50 , 0, 0, 0),
|
|
#'Neodymium' : (sys.maxint - 49 , 0, 0, 0),
|
|
#'Promethium' : (sys.maxint - 48 , 0, 0, 0),
|
|
#'Samarium' : (sys.maxint - 47 , 0, 0, 0),
|
|
#'Europium' : (sys.maxint - 46 , 0, 0, 0),
|
|
#'Gadolinium' : (sys.maxint - 45 , 0, 0, 0),
|
|
#'Terbium' : (sys.maxint - 44 , 0, 0, 0),
|
|
#'Dysprosium' : (sys.maxint - 43 , 0, 0, 0),
|
|
#'Holmium' : (sys.maxint - 42 , 0, 0, 0),
|
|
#'Erbium' : (sys.maxint - 41 , 0, 0, 0),
|
|
#'Thulium' : (sys.maxint - 40 , 0, 0, 0),
|
|
#'Ytterbium' : (sys.maxint - 39 , 0, 0, 0),
|
|
#'Lutetium' : (sys.maxint - 38 , 0, 0, 0),
|
|
#'Hafnium' : (sys.maxint - 37 , 0, 0, 0),
|
|
#'Tantalum' : (sys.maxint - 36 , 0, 0, 0),
|
|
#'Tungsten' : (sys.maxint - 35 , 0, 0, 0),
|
|
#'Rhenium' : (sys.maxint - 34 , 0, 0, 0),
|
|
#'Osmium' : (sys.maxint - 33 , 0, 0, 0),
|
|
#'Iridium' : (sys.maxint - 32 , 0, 0, 0),
|
|
#'Platinum' : (sys.maxint - 31 , 0, 0, 0),
|
|
#'Gold' : (sys.maxint - 30 , 0, 0, 0),
|
|
#'Mercury' : (sys.maxint - 29 , 0, 0, 0),
|
|
#'Thallium' : (sys.maxint - 28 , 0, 0, 0),
|
|
#'Lead' : (sys.maxint - 27 , 0, 0, 0),
|
|
#'Bismuth' : (sys.maxint - 26 , 0, 0, 0),
|
|
#'Polonium' : (sys.maxint - 25 , 0, 0, 0),
|
|
#'Astatine' : (sys.maxint - 24 , 0, 0, 0),
|
|
#'Radon' : (sys.maxint - 23 , 0, 0, 0),
|
|
#'Francium' : (sys.maxint - 22 , 0, 0, 0),
|
|
#'Radium' : (sys.maxint - 21 , 0, 0, 0),
|
|
#'Actinium' : (sys.maxint - 20 , 0, 0, 0),
|
|
#'Thorium' : (sys.maxint - 19 , 0, 0, 0),
|
|
#'Protactinium' : (sys.maxint - 18 , 0, 0, 0),
|
|
#'Uranium' : (sys.maxint - 17 , 0, 0, 0),
|
|
#'Neptunium' : (sys.maxint - 16 , 0, 0, 0),
|
|
#'Plutonium' : (sys.maxint - 15 , 0, 0, 0),
|
|
#'Americium' : (sys.maxint - 14 , 0, 0, 0),
|
|
#'Curium' : (sys.maxint - 13 , 0, 0, 0),
|
|
#'Berkelium' : (sys.maxint - 12 , 0, 0, 0),
|
|
#'Californium' : (sys.maxint - 11 , 0, 0, 0),
|
|
#'Einsteinium' : (sys.maxint - 10 , 0, 0, 0),
|
|
#'Fermium' : (sys.maxint - 9 , 0, 0, 0),
|
|
#'Mendelevium' : (sys.maxint - 8 , 0, 0, 0),
|
|
#'Nobelium' : (sys.maxint - 7 , 0, 0, 0),
|
|
#'Lawrencium' : (sys.maxint - 6 , 0, 0, 0),
|
|
#'Rutherfordium': (sys.maxint - 5 , 0, 0, 0),
|
|
#'Dubnium' : (sys.maxint - 4 , 0, 0, 0),
|
|
#'Seaborgium' : (sys.maxint - 3 , 0, 0, 0),
|
|
#'Bohrium' : (sys.maxint - 2 , 0, 0, 0),
|
|
#'Hassium' : (sys.maxint - 1 , 0, 0, 0),
|
|
#'Meitnerium' : (sys.maxint - 0 , 0, 0, 0),
|
|
# <---- Please refrain from fixing PEP-8 E203 ------------------------
|
|
}
|
|
|
|
LNAMES = dict((k.lower(), v) for (k, v) in NAMES.iteritems())
|
|
VNAMES = dict((v, k) for (k, v) in NAMES.iteritems())
|
|
|
|
def __init__(self, # pylint: disable=C0103
|
|
major,
|
|
minor,
|
|
bugfix=0,
|
|
rc=0, # pylint: disable=C0103
|
|
noc=0,
|
|
sha=None):
|
|
|
|
if isinstance(major, string_types):
|
|
major = int(major)
|
|
|
|
if isinstance(minor, string_types):
|
|
minor = int(minor)
|
|
|
|
if bugfix is None:
|
|
bugfix = 0
|
|
elif isinstance(bugfix, string_types):
|
|
bugfix = int(bugfix)
|
|
|
|
if rc is None:
|
|
rc = 0
|
|
elif isinstance(rc, string_types):
|
|
rc = int(rc)
|
|
|
|
if noc is None:
|
|
noc = 0
|
|
elif isinstance(noc, string_types):
|
|
noc = int(noc)
|
|
|
|
self.major = major
|
|
self.minor = minor
|
|
self.bugfix = bugfix
|
|
self.rc = rc # pylint: disable=C0103
|
|
self.name = self.VNAMES.get((major, minor, bugfix, rc), None)
|
|
self.noc = noc
|
|
self.sha = sha
|
|
|
|
@classmethod
|
|
def parse(cls, version_string):
|
|
if version_string.lower() in cls.LNAMES:
|
|
return cls.from_name(version_string)
|
|
match = cls.git_describe_regex.match(version_string)
|
|
if not match:
|
|
raise ValueError(
|
|
'Unable to parse version string: {0!r}'.format(version_string)
|
|
)
|
|
return cls(*match.groups())
|
|
|
|
@classmethod
|
|
def from_name(cls, name):
|
|
if name.lower() not in cls.LNAMES:
|
|
raise ValueError(
|
|
'Named version {0!r} is not known'.format(name)
|
|
)
|
|
return cls(*cls.LNAMES[name.lower()])
|
|
|
|
@property
|
|
def info(self):
|
|
return (
|
|
self.major,
|
|
self.minor,
|
|
self.bugfix
|
|
)
|
|
|
|
@property
|
|
def rc_info(self):
|
|
return (
|
|
self.major,
|
|
self.minor,
|
|
self.bugfix,
|
|
self.rc
|
|
)
|
|
|
|
@property
|
|
def noc_info(self):
|
|
return (
|
|
self.major,
|
|
self.minor,
|
|
self.bugfix,
|
|
self.rc,
|
|
self.noc
|
|
)
|
|
|
|
@property
|
|
def full_info(self):
|
|
return (
|
|
self.major,
|
|
self.minor,
|
|
self.bugfix,
|
|
self.rc,
|
|
self.noc,
|
|
self.sha
|
|
)
|
|
|
|
@property
|
|
def string(self):
|
|
version_string = '{0}.{1}.{2}'.format(
|
|
self.major,
|
|
self.minor,
|
|
self.bugfix
|
|
)
|
|
if self.rc:
|
|
version_string += 'rc{0}'.format(self.rc)
|
|
if self.noc and self.sha:
|
|
version_string += '-{0}-{1}'.format(self.noc, self.sha)
|
|
return version_string
|
|
|
|
@property
|
|
def formatted_version(self):
|
|
if self.name and self.major > 10000:
|
|
return '{0} (Unreleased)'.format(self.name)
|
|
elif self.name:
|
|
return '{0} ({1})'.format(self.name, self.string)
|
|
return self.string
|
|
|
|
def __str__(self):
|
|
return self.string
|
|
|
|
def __cmp__(self, other):
|
|
if not isinstance(other, SaltStackVersion):
|
|
if isinstance(other, string_types):
|
|
other = SaltStackVersion.parse(other)
|
|
elif isinstance(other, (list, tuple)):
|
|
other = SaltStackVersion(*other)
|
|
else:
|
|
raise ValueError(
|
|
'Cannot instantiate Version from type {0!r}'.format(
|
|
type(other)
|
|
)
|
|
)
|
|
|
|
if (self.rc and other.rc) or (not self.rc and not other.rc):
|
|
# Both have rc information, regular compare is ok
|
|
return cmp(self.noc_info, other.noc_info)
|
|
|
|
# RC's are always lower versions than non RC's
|
|
if self.rc > 0 and other.rc <= 0:
|
|
noc_info = list(self.noc_info)
|
|
noc_info[3] = -1
|
|
return cmp(tuple(noc_info), other.noc_info)
|
|
|
|
if self.rc <= 0 and other.rc > 0:
|
|
other_noc_info = list(other.noc_info)
|
|
other_noc_info[3] = -1
|
|
return cmp(self.noc_info, tuple(other_noc_info))
|
|
|
|
def __repr__(self):
|
|
parts = []
|
|
if self.name:
|
|
parts.append('name={0!r}'.format(self.name))
|
|
parts.extend([
|
|
'major={0}'.format(self.major),
|
|
'minor={0}'.format(self.minor),
|
|
'bugfix={0}'.format(self.bugfix)
|
|
])
|
|
if self.rc:
|
|
parts.append('rc={0}'.format(self.rc))
|
|
return '<{0} {1}>'.format(self.__class__.__name__, ' '.join(parts))
|
|
|
|
|
|
# ----- Hardcoded Salt Version Information ---------------------------------->
|
|
#
|
|
# Please bump version information for __saltstack_version__ on new releases
|
|
# ----------------------------------------------------------------------------
|
|
__saltstack_version__ = SaltStackVersion(2014, 1, 0)
|
|
__version_info__ = __saltstack_version__.info
|
|
__version__ = __saltstack_version__.string
|
|
# <---- Hardcoded Salt Version Information -----------------------------------
|
|
|
|
|
|
# ----- Dynamic/Runtime Salt Version Information ---------------------------->
|
|
def __get_version(version, version_info):
|
|
'''
|
|
If we can get a version provided at installation time or from Git, use
|
|
that instead, otherwise we carry on.
|
|
'''
|
|
try:
|
|
# Try to import the version information provided at install time
|
|
from salt._version import __version__, __version_info__ # pylint: disable=E0611
|
|
return __version__, __version_info__
|
|
except ImportError:
|
|
pass
|
|
|
|
# This might be a 'python setup.py develop' installation type. Let's
|
|
# discover the version information at runtime.
|
|
import os
|
|
import warnings
|
|
import subprocess
|
|
|
|
if 'SETUP_DIRNAME' in globals():
|
|
# This is from the exec() call in Salt's setup.py
|
|
cwd = SETUP_DIRNAME # pylint: disable=E0602
|
|
if not os.path.exists(os.path.join(cwd, '.git')):
|
|
# This is not a Salt git checkout!!! Don't even try to parse...
|
|
return version, version_info
|
|
else:
|
|
cwd = os.path.abspath(os.path.dirname(__file__))
|
|
if not os.path.exists(os.path.join(os.path.dirname(cwd), '.git')):
|
|
# This is not a Salt git checkout!!! Don't even try to parse...
|
|
return version, version_info
|
|
|
|
try:
|
|
kwargs = dict(
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
cwd=cwd
|
|
)
|
|
|
|
if not sys.platform.startswith('win'):
|
|
# Let's not import `salt.utils` for the above check
|
|
kwargs['close_fds'] = True
|
|
|
|
process = subprocess.Popen(
|
|
['git', 'describe', '--tags', '--match', 'v[0-9]*'], **kwargs)
|
|
out, err = process.communicate()
|
|
out = out.strip()
|
|
err = err.strip()
|
|
|
|
if not out or err:
|
|
return version, version_info
|
|
|
|
parsed_version = SaltStackVersion.parse(out)
|
|
|
|
if parsed_version.info > version_info:
|
|
warnings.warn(
|
|
'The parsed version info, `{0}`, is bigger than the one '
|
|
'defined in the file, `{1}`. Missing version bump?'.format(
|
|
parsed_version.info,
|
|
version_info
|
|
),
|
|
UserWarning,
|
|
stacklevel=2
|
|
)
|
|
return version, version_info
|
|
elif parsed_version.info < version_info:
|
|
warnings.warn(
|
|
'The parsed version info, `{0}`, is lower than the one '
|
|
'defined in the file, `{1}`.'
|
|
'In order to get the proper salt version with the git hash '
|
|
'you need to update salt\'s local git tags. Something like: '
|
|
'\'git fetch --tags\' or \'git fetch --tags upstream\' if '
|
|
'you followed salt\'s contribute documentation. The version '
|
|
'string WILL NOT include the git hash.'.format(
|
|
parsed_version.info,
|
|
version_info
|
|
),
|
|
UserWarning,
|
|
stacklevel=2
|
|
)
|
|
return version, version_info
|
|
return parsed_version.string, parsed_version.info
|
|
except OSError as os_err:
|
|
if os_err.errno != 2:
|
|
# If the errno is not 2(The system cannot find the file
|
|
# specified), raise the exception so it can be catch by the
|
|
# developers
|
|
raise
|
|
return version, version_info
|
|
|
|
|
|
# Get additional version information if available
|
|
__version__, __version_info__ = __get_version(__version__, __version_info__)
|
|
# This function has executed once, we're done with it. Delete it!
|
|
del __get_version
|
|
# <---- Dynamic/Runtime Salt Version Information -----------------------------
|
|
|
|
|
|
def versions_information(include_salt_cloud=False):
|
|
'''
|
|
Report on all of the versions for dependent software
|
|
'''
|
|
libs = [
|
|
('Salt', None, __version__),
|
|
('Python', None, sys.version.rsplit('\n')[0].strip()),
|
|
('Jinja2', 'jinja2', '__version__'),
|
|
('M2Crypto', 'M2Crypto', 'version'),
|
|
('msgpack-python', 'msgpack', 'version'),
|
|
('msgpack-pure', 'msgpack_pure', 'version'),
|
|
('pycrypto', 'Crypto', '__version__'),
|
|
('PyYAML', 'yaml', '__version__'),
|
|
('PyZMQ', 'zmq', '__version__'),
|
|
('ZMQ', 'zmq', 'zmq_version')
|
|
]
|
|
|
|
if include_salt_cloud:
|
|
libs.append(
|
|
('Apache Libcloud', 'libcloud', '__version__'),
|
|
)
|
|
|
|
for name, imp, attr in libs:
|
|
if imp is None:
|
|
yield name, attr
|
|
continue
|
|
try:
|
|
imp = __import__(imp)
|
|
version = getattr(imp, attr)
|
|
if callable(version):
|
|
version = version()
|
|
if isinstance(version, (tuple, list)):
|
|
version = '.'.join(map(str, version))
|
|
yield name, version
|
|
except ImportError:
|
|
yield name, None
|
|
|
|
|
|
def versions_report(include_salt_cloud=False):
|
|
'''
|
|
Yield each library properly formatted for a console clean output.
|
|
'''
|
|
libs = list(versions_information(include_salt_cloud=include_salt_cloud))
|
|
|
|
padding = max(len(lib[0]) for lib in libs) + 1
|
|
|
|
fmt = '{0:>{pad}}: {1}'
|
|
|
|
for name, version in libs:
|
|
yield fmt.format(name, version or 'Not Installed', pad=padding)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
print(__version__)
|