mirror of
https://github.com/valitydev/salt.git
synced 2024-11-08 17:33:54 +00:00
Merge remote-tracking branch 'upstream/develop' into feature_sqlite3_returner
This commit is contained in:
commit
5601cbb3af
@ -77,7 +77,7 @@ of libvirt certificates. These certificates allow for virtual machine
|
|||||||
migration. Salt comes with a system used to auto deploy these certificates.
|
migration. Salt comes with a system used to auto deploy these certificates.
|
||||||
Salt manages the signing authority key and generates keys for libvirt clients
|
Salt manages the signing authority key and generates keys for libvirt clients
|
||||||
on the master, signs them with the certificate authority and uses pillar to
|
on the master, signs them with the certificate authority and uses pillar to
|
||||||
distrbute them. This is managed via the ``libvirt`` state. Simply execute this
|
distribute them. This is managed via the ``libvirt`` state. Simply execute this
|
||||||
formula on the minion to ensure that the certificate is in place and up to
|
formula on the minion to ensure that the certificate is in place and up to
|
||||||
date:
|
date:
|
||||||
|
|
||||||
|
@ -616,11 +616,11 @@ def prep_trans_tar(opts, chunks, file_refs):
|
|||||||
break
|
break
|
||||||
files = file_client.cache_dir(name, env, True)
|
files = file_client.cache_dir(name, env, True)
|
||||||
if files:
|
if files:
|
||||||
for file in files:
|
for filename in files:
|
||||||
tgt = os.path.join(
|
tgt = os.path.join(
|
||||||
env_root,
|
env_root,
|
||||||
short,
|
short,
|
||||||
file[file.find(short) + len(short):],
|
filename[filename.find(short) + len(short):],
|
||||||
)
|
)
|
||||||
tgt_dir = os.path.dirname(tgt)
|
tgt_dir = os.path.dirname(tgt)
|
||||||
if not os.path.isdir(tgt_dir):
|
if not os.path.isdir(tgt_dir):
|
||||||
|
@ -128,11 +128,11 @@ def prep_trans_tar(opts, chunks, file_refs):
|
|||||||
break
|
break
|
||||||
files = file_client.cache_dir(name, env, True)
|
files = file_client.cache_dir(name, env, True)
|
||||||
if files:
|
if files:
|
||||||
for file in files:
|
for filename in files:
|
||||||
tgt = os.path.join(
|
tgt = os.path.join(
|
||||||
env_root,
|
env_root,
|
||||||
short,
|
short,
|
||||||
file[file.find(short) + len(short):],
|
filename[filename.find(short) + len(short):],
|
||||||
)
|
)
|
||||||
tgt_dir = os.path.dirname(tgt)
|
tgt_dir = os.path.dirname(tgt)
|
||||||
if not os.path.isdir(tgt_dir):
|
if not os.path.isdir(tgt_dir):
|
||||||
|
@ -190,7 +190,7 @@ def fileserver(opts, backends):
|
|||||||
|
|
||||||
def roster(opts, whitelist=None):
|
def roster(opts, whitelist=None):
|
||||||
'''
|
'''
|
||||||
Returns the file server modules
|
Returns the roster modules
|
||||||
'''
|
'''
|
||||||
load = _create_loader(opts, 'roster', 'roster')
|
load = _create_loader(opts, 'roster', 'roster')
|
||||||
ret = load.gen_functions(whitelist=whitelist)
|
ret = load.gen_functions(whitelist=whitelist)
|
||||||
|
@ -1896,7 +1896,7 @@ class ClearFuncs(object):
|
|||||||
self.event.fire_event(data, tagify([jid, 'new'], 'wheel'))
|
self.event.fire_event(data, tagify([jid, 'new'], 'wheel'))
|
||||||
ret = self.wheel_.call_func(fun, **clear_load.get('kwarg', {}))
|
ret = self.wheel_.call_func(fun, **clear_load.get('kwarg', {}))
|
||||||
data['ret'] = ret
|
data['ret'] = ret
|
||||||
self.event.fire_event(data, tagify([jid, 'ret'], 'wheel'))
|
self.event.fire_event(data, tagify([jid, 'ret'], 'wheel'))
|
||||||
return data
|
return data
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
log.error(exc)
|
log.error(exc)
|
||||||
@ -1915,7 +1915,7 @@ class ClearFuncs(object):
|
|||||||
'user {0}.').format(clear_load.get('username', 'UNKNOWN'))
|
'user {0}.').format(clear_load.get('username', 'UNKNOWN'))
|
||||||
log.warning(msg)
|
log.warning(msg)
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
name = self.loadauth.load_name(clear_load)
|
name = self.loadauth.load_name(clear_load)
|
||||||
if not ((name in self.opts['external_auth'][clear_load['eauth']]) | ('*' in self.opts['external_auth'][clear_load['eauth']])):
|
if not ((name in self.opts['external_auth'][clear_load['eauth']]) | ('*' in self.opts['external_auth'][clear_load['eauth']])):
|
||||||
|
@ -23,6 +23,11 @@ __outputter__ = {
|
|||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Don't shadow built-in's.
|
||||||
|
__func_alias__ = {
|
||||||
|
'set_': 'set'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def __virtual__():
|
def __virtual__():
|
||||||
'''
|
'''
|
||||||
@ -151,7 +156,7 @@ def auto(name):
|
|||||||
return out['stdout']
|
return out['stdout']
|
||||||
|
|
||||||
|
|
||||||
def set(name, path):
|
def set_(name, path):
|
||||||
'''
|
'''
|
||||||
Manually set the alternative <path> for <name>.
|
Manually set the alternative <path> for <name>.
|
||||||
|
|
||||||
|
@ -6,15 +6,15 @@ access to the master root execution access to all salt minions
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
# Import python libs
|
# Import python libs
|
||||||
|
import functools
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import functools
|
|
||||||
import sys
|
import sys
|
||||||
import json
|
|
||||||
import yaml
|
|
||||||
import traceback
|
import traceback
|
||||||
|
import yaml
|
||||||
|
|
||||||
# Import salt libs
|
# Import salt libs
|
||||||
import salt.utils
|
import salt.utils
|
||||||
@ -174,8 +174,9 @@ def _run(cmd,
|
|||||||
'''
|
'''
|
||||||
Do the DRY thing and only call subprocess.Popen() once
|
Do the DRY thing and only call subprocess.Popen() once
|
||||||
'''
|
'''
|
||||||
# Set the default working directory to the home directory
|
# Set the default working directory to the home directory of the user
|
||||||
# of the user salt-minion is running as. Default: /root
|
# salt-minion is running as. Defaults to home directory of user under which
|
||||||
|
# the minion is running.
|
||||||
if not cwd:
|
if not cwd:
|
||||||
cwd = os.path.expanduser('~{0}'.format('' if not runas else runas))
|
cwd = os.path.expanduser('~{0}'.format('' if not runas else runas))
|
||||||
|
|
||||||
@ -187,6 +188,10 @@ def _run(cmd,
|
|||||||
cwd = '/'
|
cwd = '/'
|
||||||
if salt.utils.is_windows():
|
if salt.utils.is_windows():
|
||||||
cwd = os.tempnam()[:3]
|
cwd = os.tempnam()[:3]
|
||||||
|
else:
|
||||||
|
# Handle edge cases where numeric/other input is entered, and would be
|
||||||
|
# yaml-ified into non-string types
|
||||||
|
cwd = str(cwd)
|
||||||
|
|
||||||
if not salt.utils.is_windows():
|
if not salt.utils.is_windows():
|
||||||
if not os.path.isfile(shell) or not os.access(shell, os.X_OK):
|
if not os.path.isfile(shell) or not os.access(shell, os.X_OK):
|
||||||
@ -296,7 +301,7 @@ def _run(cmd,
|
|||||||
'stdin': str(stdin) if stdin is not None else stdin,
|
'stdin': str(stdin) if stdin is not None else stdin,
|
||||||
'stdout': stdout,
|
'stdout': stdout,
|
||||||
'stderr': stderr,
|
'stderr': stderr,
|
||||||
'with_communicate' : with_communicate}
|
'with_communicate': with_communicate}
|
||||||
|
|
||||||
if umask:
|
if umask:
|
||||||
try:
|
try:
|
||||||
@ -321,8 +326,18 @@ def _run(cmd,
|
|||||||
kwargs['executable'] = shell
|
kwargs['executable'] = shell
|
||||||
kwargs['close_fds'] = True
|
kwargs['close_fds'] = True
|
||||||
|
|
||||||
|
if not os.path.isabs(cwd) or not os.path.isdir(cwd):
|
||||||
|
raise CommandExecutionError(
|
||||||
|
'Specified cwd {0!r} either not absolute or does not exist'
|
||||||
|
.format(cwd)
|
||||||
|
)
|
||||||
|
|
||||||
# This is where the magic happens
|
# This is where the magic happens
|
||||||
proc = salt.utils.timed_subprocess.TimedProc(cmd, **kwargs)
|
try:
|
||||||
|
proc = salt.utils.timed_subprocess.TimedProc(cmd, **kwargs)
|
||||||
|
except (OSError, IOError) as exc:
|
||||||
|
raise CommandExecutionError('Unable to run command: {0}'.format(exc))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
proc.wait(timeout)
|
proc.wait(timeout)
|
||||||
except salt.exceptions.TimedProcTimeoutError, e:
|
except salt.exceptions.TimedProcTimeoutError, e:
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
'''
|
'''
|
||||||
Manage information about regular files, directories,
|
Manage information about regular files, directories,
|
||||||
and special files on the minion, set/read user,
|
and special files on the minion, set/read user,
|
||||||
group, mode, and data
|
group, mode, and data
|
||||||
'''
|
'''
|
||||||
|
@ -15,6 +15,11 @@ import salt.utils
|
|||||||
# Set up logging
|
# Set up logging
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Don't shadow built-in's.
|
||||||
|
__func_alias__ = {
|
||||||
|
'list_': 'list'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def __virtual__():
|
def __virtual__():
|
||||||
if not salt.utils.which('lxc'):
|
if not salt.utils.which('lxc'):
|
||||||
@ -217,7 +222,7 @@ def create(name, config=None, profile=None, options=None, **kwargs):
|
|||||||
return {'created': False, 'error': 'container could not be created'}
|
return {'created': False, 'error': 'container could not be created'}
|
||||||
|
|
||||||
|
|
||||||
def list():
|
def list_():
|
||||||
'''
|
'''
|
||||||
List defined containers (running, stopped, and frozen).
|
List defined containers (running, stopped, and frozen).
|
||||||
|
|
||||||
|
@ -203,7 +203,7 @@ def list_running_members(lbn, profile='default'):
|
|||||||
salt '*' modjk.list_running_members loadbalancer1 other-profile
|
salt '*' modjk.list_running_members loadbalancer1 other-profile
|
||||||
'''
|
'''
|
||||||
|
|
||||||
config = get_running()
|
config = get_running(profile)
|
||||||
try:
|
try:
|
||||||
return config['worker.{0}.balance_workers'.format(lbn)].split(',')
|
return config['worker.{0}.balance_workers'.format(lbn)].split(',')
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -727,7 +727,9 @@ def list_(prefix=None,
|
|||||||
'''
|
'''
|
||||||
packages = {}
|
packages = {}
|
||||||
|
|
||||||
cmd = [_get_pip_bin(bin_env), 'freeze']
|
pip_bin = _get_pip_bin(bin_env)
|
||||||
|
pip_version_cmd = [pip_bin, '--version']
|
||||||
|
cmd = [pip_bin, 'freeze']
|
||||||
|
|
||||||
if runas is not None:
|
if runas is not None:
|
||||||
# The user is using a deprecated argument, warn!
|
# The user is using a deprecated argument, warn!
|
||||||
@ -751,6 +753,14 @@ def list_(prefix=None,
|
|||||||
cmd_kwargs = dict(runas=user, cwd=cwd)
|
cmd_kwargs = dict(runas=user, cwd=cwd)
|
||||||
if bin_env and os.path.isdir(bin_env):
|
if bin_env and os.path.isdir(bin_env):
|
||||||
cmd_kwargs['env'] = {'VIRTUAL_ENV': bin_env}
|
cmd_kwargs['env'] = {'VIRTUAL_ENV': bin_env}
|
||||||
|
|
||||||
|
if not prefix or prefix in ('p', 'pi', 'pip'):
|
||||||
|
pip_version_result = __salt__['cmd.run_all'](' '.join(pip_version_cmd),
|
||||||
|
**cmd_kwargs)
|
||||||
|
if pip_version_result['retcode'] > 0:
|
||||||
|
raise CommandExecutionError(pip_version_result['stderr'])
|
||||||
|
packages['pip'] = pip_version_result['stdout'].split()[1]
|
||||||
|
|
||||||
result = __salt__['cmd.run_all'](' '.join(cmd), **cmd_kwargs)
|
result = __salt__['cmd.run_all'](' '.join(cmd), **cmd_kwargs)
|
||||||
if result['retcode'] > 0:
|
if result['retcode'] > 0:
|
||||||
raise CommandExecutionError(result['stderr'])
|
raise CommandExecutionError(result['stderr'])
|
||||||
|
@ -378,7 +378,7 @@ def list_queues_vhost(vhost, *kwargs):
|
|||||||
|
|
||||||
def list_policies(runas=None):
|
def list_policies(runas=None):
|
||||||
'''
|
'''
|
||||||
Return a dictionary of policies nested by vhost and name
|
Return a dictionary of policies nested by vhost and name
|
||||||
based on the data returned from rabbitmqctl list_policies.
|
based on the data returned from rabbitmqctl list_policies.
|
||||||
|
|
||||||
Reference: http://www.rabbitmq.com/ha.html
|
Reference: http://www.rabbitmq.com/ha.html
|
||||||
|
@ -19,6 +19,11 @@ import salt.config
|
|||||||
# Set up logging
|
# Set up logging
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Don't shadow built-in's.
|
||||||
|
__func_alias__ = {
|
||||||
|
'apply_': 'apply'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _mount(path, ftype):
|
def _mount(path, ftype):
|
||||||
mpt = None
|
mpt = None
|
||||||
@ -44,7 +49,7 @@ def _umount(mpt, ftype):
|
|||||||
__salt__['img.umount_image'](mpt)
|
__salt__['img.umount_image'](mpt)
|
||||||
|
|
||||||
|
|
||||||
def apply(path, id_=None, config=None, approve_key=True, install=True):
|
def apply_(path, id_=None, config=None, approve_key=True, install=True):
|
||||||
'''
|
'''
|
||||||
Seed a location (disk image, directory, or block device) with the
|
Seed a location (disk image, directory, or block device) with the
|
||||||
minion config, approve the minion's key, and/or install salt-minion.
|
minion config, approve the minion's key, and/or install salt-minion.
|
||||||
|
@ -166,7 +166,7 @@ def _wget(cmd, opts=None, url='http://localhost:8080/manager', timeout=180):
|
|||||||
ret['msg'] = urllib2.urlopen(url6, timeout=timeout).read().splitlines()
|
ret['msg'] = urllib2.urlopen(url6, timeout=timeout).read().splitlines()
|
||||||
except Exception:
|
except Exception:
|
||||||
ret['msg'] = 'Failed to create HTTP request'
|
ret['msg'] = 'Failed to create HTTP request'
|
||||||
|
|
||||||
if not ret['msg'][0].startswith('OK'):
|
if not ret['msg'][0].startswith('OK'):
|
||||||
ret['res'] = False
|
ret['res'] = False
|
||||||
|
|
||||||
|
@ -293,7 +293,7 @@ def restart(name, **kwargs):
|
|||||||
return not __salt__['cmd.retcode'](cmd)
|
return not __salt__['cmd.retcode'](cmd)
|
||||||
|
|
||||||
|
|
||||||
def full_restart(name, **kwargs):
|
def full_restart(name):
|
||||||
'''
|
'''
|
||||||
Do a full restart (stop/start) of the named service
|
Do a full restart (stop/start) of the named service
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ ext_pillar:
|
|||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import time
|
|
||||||
|
|
||||||
# Import third party libs
|
# Import third party libs
|
||||||
HAS_GIT = False
|
HAS_GIT = False
|
||||||
@ -23,7 +22,6 @@ except ImportError:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
# Import salt libs
|
# Import salt libs
|
||||||
import salt.utils
|
|
||||||
from salt.pillar import Pillar
|
from salt.pillar import Pillar
|
||||||
|
|
||||||
# Set up logging
|
# Set up logging
|
||||||
|
@ -3,6 +3,51 @@ Pure python state renderer
|
|||||||
|
|
||||||
The sls file should contain a function called ``run`` which returns high state
|
The sls file should contain a function called ``run`` which returns high state
|
||||||
data
|
data
|
||||||
|
|
||||||
|
In this module, a few objects are defined for you, including the usual
|
||||||
|
(with``__`` added) ``__salt__`` dictionary, ``__grains__``,
|
||||||
|
``__pillar__``, ``__opts__``, ``__env__``, and ``__sls__``.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:linenos:
|
||||||
|
|
||||||
|
#!py
|
||||||
|
|
||||||
|
def run():
|
||||||
|
config = {}
|
||||||
|
|
||||||
|
if __grains__['os'] == 'Ubuntu':
|
||||||
|
user = 'ubuntu'
|
||||||
|
group = 'ubuntu'
|
||||||
|
home = '/home/{0}'.format(user)
|
||||||
|
else:
|
||||||
|
user = 'root'
|
||||||
|
group = 'root'
|
||||||
|
home = '/root/'
|
||||||
|
|
||||||
|
config['s3cmd'] = {
|
||||||
|
'pkg': [
|
||||||
|
'installed',
|
||||||
|
{'name': 's3cmd'},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
config[home + '/.s3cfg'} = {
|
||||||
|
'file.managed': [{
|
||||||
|
'source': 'salt://s3cfg/templates/s3cfg',
|
||||||
|
'template': 'jinja',
|
||||||
|
'user': user,
|
||||||
|
'group': group,
|
||||||
|
'mode': 600,
|
||||||
|
'context': {
|
||||||
|
'aws_key': __pillar__['AWS_ACCESS_KEY_ID'],
|
||||||
|
'aws_secret_key': __pillar__['AWS_SECRET_ACCESS_KEY'],
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# Import python libs
|
# Import python libs
|
||||||
@ -26,12 +71,12 @@ def render(template, env='', sls='', tmplpath=None, **kws):
|
|||||||
tmp_data = salt.utils.templates.py(
|
tmp_data = salt.utils.templates.py(
|
||||||
template,
|
template,
|
||||||
True,
|
True,
|
||||||
salt=__salt__,
|
__salt__=__salt__,
|
||||||
grains=__grains__,
|
__grains__=__grains__,
|
||||||
opts=__opts__,
|
__opts__=__opts__,
|
||||||
pillar=__pillar__,
|
__pillar__=__pillar__,
|
||||||
env=env,
|
__env__=env,
|
||||||
sls=sls,
|
__sls__=sls,
|
||||||
**kws)
|
**kws)
|
||||||
if not tmp_data.get('result', False):
|
if not tmp_data.get('result', False):
|
||||||
raise SaltRenderError(tmp_data.get('data',
|
raise SaltRenderError(tmp_data.get('data',
|
||||||
|
@ -144,7 +144,7 @@ def auto(name):
|
|||||||
if line.endswith(' auto mode'):
|
if line.endswith(' auto mode'):
|
||||||
ret['comment'] = '{0} already in auto mode'.format(name)
|
ret['comment'] = '{0} already in auto mode'.format(name)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
ret['changes']['result'] = __salt__['alternatives.auto'](name)
|
ret['changes']['result'] = __salt__['alternatives.auto'](name)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@ -172,14 +172,14 @@ def set(name, path):
|
|||||||
if current == path:
|
if current == path:
|
||||||
ret['comment'] = 'Alternative for {0} already set to {1}'.format(name, path)
|
ret['comment'] = 'Alternative for {0} already set to {1}'.format(name, path)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
display = __salt__['alternatives.display'](name)
|
display = __salt__['alternatives.display'](name)
|
||||||
isinstalled = False
|
isinstalled = False
|
||||||
for line in display.splitlines():
|
for line in display.splitlines():
|
||||||
if line.startswith(path):
|
if line.startswith(path):
|
||||||
isinstalled = True
|
isinstalled = True
|
||||||
break
|
break
|
||||||
|
|
||||||
if isinstalled:
|
if isinstalled:
|
||||||
__salt__['alternatives.set'](name, path)
|
__salt__['alternatives.set'](name, path)
|
||||||
current = __salt__['alternatives.show_current'](name)
|
current = __salt__['alternatives.show_current'](name)
|
||||||
|
@ -64,13 +64,13 @@ In this example ``foo.conf`` in the ``dev`` environment will be used instead.
|
|||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
When using a mode that includes a leading zero you must wrap the
|
When using a mode that includes a leading zero you must wrap the
|
||||||
value in single quotes. If the value is not wrapped in quotes it
|
value in single quotes. If the value is not wrapped in quotes it
|
||||||
will be read by YAML as an integer and evaluated as an octal.
|
will be read by YAML as an integer and evaluated as an octal.
|
||||||
|
|
||||||
Special files can be managed via the ``mknod`` function. This function will
|
Special files can be managed via the ``mknod`` function. This function will
|
||||||
create and enforce the permissions on a special file. The function supports the
|
create and enforce the permissions on a special file. The function supports the
|
||||||
creation of character devices, block devices, and fifo pipes. The function will
|
creation of character devices, block devices, and fifo pipes. The function will
|
||||||
create the directory structure up to the special file if it is needed on the
|
create the directory structure up to the special file if it is needed on the
|
||||||
minion. The function will not overwrite or operate on (change major/minor
|
minion. The function will not overwrite or operate on (change major/minor
|
||||||
numbers) existing special files with the exception of user, group, and
|
numbers) existing special files with the exception of user, group, and
|
||||||
@ -2468,9 +2468,9 @@ def mknod(name, ntype, major=0, minor=0, user=None, group=None, mode='0600'):
|
|||||||
Create a special file similar to the 'nix mknod command. The supported device types are
|
Create a special file similar to the 'nix mknod command. The supported device types are
|
||||||
p (fifo pipe), c (character device), and b (block device). Provide the major and minor
|
p (fifo pipe), c (character device), and b (block device). Provide the major and minor
|
||||||
numbers when specifying a character device or block device. A fifo pipe does not require
|
numbers when specifying a character device or block device. A fifo pipe does not require
|
||||||
this information. The command will create the necessary dirs if needed. If a file of the
|
this information. The command will create the necessary dirs if needed. If a file of the
|
||||||
same name not of the same type/major/minor exists, it will not be overwritten or unlinked
|
same name not of the same type/major/minor exists, it will not be overwritten or unlinked
|
||||||
(deleted). This is logically in place as a safety measure because you can really shoot
|
(deleted). This is logically in place as a safety measure because you can really shoot
|
||||||
yourself in the foot here and it is the behavior of 'nix mknod. It is also important to
|
yourself in the foot here and it is the behavior of 'nix mknod. It is also important to
|
||||||
note that not just anyone can create special devices. Usually this is only done as root.
|
note that not just anyone can create special devices. Usually this is only done as root.
|
||||||
If the state is executed as none other than root on a minion, you may recieve a permision
|
If the state is executed as none other than root on a minion, you may recieve a permision
|
||||||
@ -2509,7 +2509,7 @@ def mknod(name, ntype, major=0, minor=0, user=None, group=None, mode='0600'):
|
|||||||
- user: root
|
- user: root
|
||||||
- group: root
|
- group: root
|
||||||
- mode: 660
|
- mode: 660
|
||||||
|
|
||||||
/dev/blk:
|
/dev/blk:
|
||||||
file.mknod:
|
file.mknod:
|
||||||
- ntype: b
|
- ntype: b
|
||||||
@ -2518,7 +2518,7 @@ def mknod(name, ntype, major=0, minor=0, user=None, group=None, mode='0600'):
|
|||||||
- user: root
|
- user: root
|
||||||
- group: root
|
- group: root
|
||||||
- mode: 660
|
- mode: 660
|
||||||
|
|
||||||
/dev/fifo:
|
/dev/fifo:
|
||||||
file.mknod:
|
file.mknod:
|
||||||
- ntype: p
|
- ntype: p
|
||||||
|
@ -14,12 +14,6 @@ Management of Keystone users.
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# Import python libs
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# Import salt libs
|
|
||||||
import salt.utils
|
|
||||||
|
|
||||||
|
|
||||||
def __virtual__():
|
def __virtual__():
|
||||||
'''
|
'''
|
||||||
@ -144,7 +138,7 @@ def tenant_present(name, description=None, enabled=True):
|
|||||||
|
|
||||||
# Check if user is already present
|
# Check if user is already present
|
||||||
tenant = __salt__['keystone.tenant_get'](name=name)
|
tenant = __salt__['keystone.tenant_get'](name=name)
|
||||||
|
|
||||||
if 'Error' not in tenant:
|
if 'Error' not in tenant:
|
||||||
if tenant[name]['description'] != description:
|
if tenant[name]['description'] != description:
|
||||||
__salt__['keystone.tenant_update'](name, description, enabled)
|
__salt__['keystone.tenant_update'](name, description, enabled)
|
||||||
|
@ -38,7 +38,8 @@ def latest(name,
|
|||||||
username=None,
|
username=None,
|
||||||
password=None,
|
password=None,
|
||||||
force=False,
|
force=False,
|
||||||
externals=True):
|
externals=True,
|
||||||
|
trust=False):
|
||||||
'''
|
'''
|
||||||
Checkout or update the working directory to the latest revision from the
|
Checkout or update the working directory to the latest revision from the
|
||||||
remote repository.
|
remote repository.
|
||||||
@ -69,6 +70,9 @@ def latest(name,
|
|||||||
|
|
||||||
externals : True
|
externals : True
|
||||||
Change to False to not checkout or update externals
|
Change to False to not checkout or update externals
|
||||||
|
|
||||||
|
trust : False
|
||||||
|
Automatically trust the remote server. SVN's --trust-server-cert
|
||||||
'''
|
'''
|
||||||
ret = {'name': name, 'result': True, 'comment': '', 'changes': {}}
|
ret = {'name': name, 'result': True, 'comment': '', 'changes': {}}
|
||||||
if not target:
|
if not target:
|
||||||
@ -109,6 +113,9 @@ def latest(name,
|
|||||||
if externals is False:
|
if externals is False:
|
||||||
opts += ('--ignore-externals',)
|
opts += ('--ignore-externals',)
|
||||||
|
|
||||||
|
if trust:
|
||||||
|
opts += ('--trust-server-cert',)
|
||||||
|
|
||||||
if svn_cmd == 'svn.update':
|
if svn_cmd == 'svn.update':
|
||||||
out = __salt__[svn_cmd](cwd, basename, user, username, password, *opts)
|
out = __salt__[svn_cmd](cwd, basename, user, username, password, *opts)
|
||||||
|
|
||||||
@ -121,6 +128,7 @@ def latest(name,
|
|||||||
fmt='dict')[0]['Revision']
|
fmt='dict')[0]['Revision']
|
||||||
if current_rev != new_rev:
|
if current_rev != new_rev:
|
||||||
ret['changes']['revision'] = "{0} => {1}".format(current_rev, new_rev)
|
ret['changes']['revision'] = "{0} => {1}".format(current_rev, new_rev)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
out = __salt__[svn_cmd](cwd, name, basename, user, username, password, *opts)
|
out = __salt__[svn_cmd](cwd, name, basename, user, username, password, *opts)
|
||||||
|
|
||||||
|
@ -1341,7 +1341,7 @@ def warn_until(version_info,
|
|||||||
:param category: The warning class to be thrown, by default
|
:param category: The warning class to be thrown, by default
|
||||||
``DeprecationWarning``
|
``DeprecationWarning``
|
||||||
:param stacklevel: There should be no need to set the value of
|
:param stacklevel: There should be no need to set the value of
|
||||||
``stacklevel`` salt should be able to do the right thing
|
``stacklevel``. Salt should be able to do the right thing.
|
||||||
:param _version_info_: In order to reuse this function for other SaltStack
|
:param _version_info_: In order to reuse this function for other SaltStack
|
||||||
projects, they need to be able to provide the
|
projects, they need to be able to provide the
|
||||||
version info to compare to.
|
version info to compare to.
|
||||||
@ -1380,6 +1380,65 @@ def warn_until(version_info,
|
|||||||
warnings.warn(message, category, stacklevel=stacklevel)
|
warnings.warn(message, category, stacklevel=stacklevel)
|
||||||
|
|
||||||
|
|
||||||
|
def kwargs_warn_until(kwargs,
|
||||||
|
version_info,
|
||||||
|
category=DeprecationWarning,
|
||||||
|
stacklevel=None,
|
||||||
|
_version_info_=None,
|
||||||
|
_dont_call_warnings=False):
|
||||||
|
'''
|
||||||
|
Helper function to raise a warning (by default, a ``DeprecationWarning``)
|
||||||
|
when unhandled keyword arguments are passed to function, until the
|
||||||
|
provided ``version_info``, after which, a ``RuntimeError`` will be raised
|
||||||
|
to remind the developers to remove the ``**kwargs`` because the target
|
||||||
|
version has been reached.
|
||||||
|
This function is used to help deprecate unused legacy ``**kwargs`` that
|
||||||
|
were added to function parameters lists to preserve backwards compatibility
|
||||||
|
when removing a parameter. See
|
||||||
|
:doc:`the deprecation development docs </topics/development/deprecations>`
|
||||||
|
for the modern strategy for deprecating a function parameter.
|
||||||
|
|
||||||
|
:param kwargs: The caller's ``**kwargs`` argument value (a ``dict``).
|
||||||
|
:param version_info: The version info after which the warning becomes a
|
||||||
|
``RuntimeError``. For example ``(0, 17)``.
|
||||||
|
:param category: The warning class to be thrown, by default
|
||||||
|
``DeprecationWarning``
|
||||||
|
:param stacklevel: There should be no need to set the value of
|
||||||
|
``stacklevel``. Salt should be able to do the right thing.
|
||||||
|
:param _version_info_: In order to reuse this function for other SaltStack
|
||||||
|
projects, they need to be able to provide the
|
||||||
|
version info to compare to.
|
||||||
|
:param _dont_call_warnings: This parameter is used just to get the
|
||||||
|
functionality until the actual error is to be
|
||||||
|
issued. When we're only after the salt version
|
||||||
|
checks to raise a ``RuntimeError``.
|
||||||
|
'''
|
||||||
|
if not isinstance(version_info, tuple):
|
||||||
|
raise RuntimeError(
|
||||||
|
'The \'version_info\' argument should be passed as a tuple.'
|
||||||
|
)
|
||||||
|
|
||||||
|
if stacklevel is None:
|
||||||
|
# Attribute the warning to the calling function,
|
||||||
|
# not to kwargs_warn_until() or warn_until()
|
||||||
|
stacklevel = 3
|
||||||
|
|
||||||
|
if _version_info_ is None:
|
||||||
|
_version_info_ = salt.version.__version_info__
|
||||||
|
|
||||||
|
if kwargs or _version_info_ >= version_info:
|
||||||
|
removal_version = '.'.join(str(component) for component in version_info)
|
||||||
|
arg_names = ', '.join('\'{0}\''.format(key) for key in kwargs)
|
||||||
|
warn_until(version_info,
|
||||||
|
message='The following parameter(s) have been deprecated and '
|
||||||
|
'will be removed in {0}: {1}.'.format(removal_version, arg_names),
|
||||||
|
category=category,
|
||||||
|
stacklevel=stacklevel,
|
||||||
|
_version_info_=_version_info_,
|
||||||
|
_dont_call_warnings=_dont_call_warnings
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def version_cmp(pkg1, pkg2):
|
def version_cmp(pkg1, pkg2):
|
||||||
'''
|
'''
|
||||||
Compares two version strings using distutils.version.LooseVersion. This is
|
Compares two version strings using distutils.version.LooseVersion. This is
|
||||||
|
@ -162,7 +162,7 @@ def _interfaces_ip(out):
|
|||||||
for line in group.splitlines():
|
for line in group.splitlines():
|
||||||
if not ' ' in line:
|
if not ' ' in line:
|
||||||
continue
|
continue
|
||||||
match = re.match(r'^\d*:\s+([\w.]+)(?:@)?(\w+)?:\s+<(.+)>', line)
|
match = re.match(r'^\d*:\s+([\w.]+)(?:@)?([\w.]+)?:\s+<(.+)>', line)
|
||||||
if match:
|
if match:
|
||||||
iface, parent, attrs = match.groups()
|
iface, parent, attrs = match.groups()
|
||||||
if 'UP' in attrs.split(','):
|
if 'UP' in attrs.split(','):
|
||||||
|
@ -1422,6 +1422,13 @@ class SaltCallOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
|
|||||||
help=('Exit with the salt call retcode and not the salt binary '
|
help=('Exit with the salt call retcode and not the salt binary '
|
||||||
'retcode')
|
'retcode')
|
||||||
)
|
)
|
||||||
|
self.add_option(
|
||||||
|
'--id',
|
||||||
|
default='',
|
||||||
|
dest='id',
|
||||||
|
help=('Specify the minion id to use. If this option is omitted, '
|
||||||
|
'the id option from the minion config will be used.')
|
||||||
|
)
|
||||||
|
|
||||||
def _mixin_after_parsed(self):
|
def _mixin_after_parsed(self):
|
||||||
if not self.args and not self.options.grains_run \
|
if not self.args and not self.options.grains_run \
|
||||||
|
@ -135,6 +135,7 @@ class TestDaemon(object):
|
|||||||
self.sub_minion_opts = salt.config.minion_config(
|
self.sub_minion_opts = salt.config.minion_config(
|
||||||
os.path.join(INTEGRATION_TEST_DIR, 'files', 'conf', 'sub_minion')
|
os.path.join(INTEGRATION_TEST_DIR, 'files', 'conf', 'sub_minion')
|
||||||
)
|
)
|
||||||
|
self.sub_minion_opts['root_dir'] = os.path.join(TMP, 'subsalt')
|
||||||
self.sub_minion_opts['user'] = running_tests_user
|
self.sub_minion_opts['user'] = running_tests_user
|
||||||
#if sys.version_info < (2, 7):
|
#if sys.version_info < (2, 7):
|
||||||
# self.sub_minion_opts['multiprocessing'] = False
|
# self.sub_minion_opts['multiprocessing'] = False
|
||||||
|
@ -200,6 +200,22 @@ sys.stdout.write('cheese')
|
|||||||
'hello' == self.run_function(
|
'hello' == self.run_function(
|
||||||
'cmd.run', ['sleep 1 && echo hello', 'timeout=2']))
|
'cmd.run', ['sleep 1 && echo hello', 'timeout=2']))
|
||||||
|
|
||||||
|
def test_run_cwd_doesnt_exist_issue_7154(self):
|
||||||
|
'''
|
||||||
|
cmd.run should fail and raise
|
||||||
|
salt.exceptions.CommandExecutionError if the cwd dir does not
|
||||||
|
exist
|
||||||
|
'''
|
||||||
|
from salt.exceptions import CommandExecutionError
|
||||||
|
import salt.modules.cmdmod as cmdmod
|
||||||
|
cmd = 'echo OHAI'
|
||||||
|
cwd = '/path/to/nowhere'
|
||||||
|
try:
|
||||||
|
cmdmod.run_all(cmd, cwd=cwd)
|
||||||
|
except CommandExecutionError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise RuntimeError
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
from integration import run_tests
|
from integration import run_tests
|
||||||
|
@ -318,6 +318,68 @@ class PipStateTest(integration.ModuleCase, integration.SaltReturnAssertsMixIn):
|
|||||||
os.unlink(req_filename)
|
os.unlink(req_filename)
|
||||||
# <---- Using user ---------------------------------------------------
|
# <---- Using user ---------------------------------------------------
|
||||||
|
|
||||||
|
def test_issue_6833_pip_upgrade_pip(self):
|
||||||
|
# Create the testing virtualenv
|
||||||
|
venv_dir = os.path.join(
|
||||||
|
integration.TMP, '6833-pip-upgrade-pip'
|
||||||
|
)
|
||||||
|
ret = self.run_function('virtualenv.create', [venv_dir])
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
self.assertEqual(ret['retcode'], 0)
|
||||||
|
self.assertIn(
|
||||||
|
'New python executable',
|
||||||
|
ret['stdout']
|
||||||
|
)
|
||||||
|
except AssertionError:
|
||||||
|
import pprint
|
||||||
|
pprint.pprint(ret)
|
||||||
|
raise
|
||||||
|
|
||||||
|
# Let's install a fixed version pip over whatever pip was
|
||||||
|
# previously installed
|
||||||
|
ret = self.run_function(
|
||||||
|
'pip.install', ['pip==1.3.1'], upgrade=True,
|
||||||
|
ignore_installed=True,
|
||||||
|
bin_env=venv_dir
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
self.assertEqual(ret['retcode'], 0)
|
||||||
|
self.assertIn(
|
||||||
|
'Successfully installed pip',
|
||||||
|
ret['stdout']
|
||||||
|
)
|
||||||
|
except AssertionError:
|
||||||
|
import pprint
|
||||||
|
pprint.pprint(ret)
|
||||||
|
raise
|
||||||
|
|
||||||
|
# Le't make sure we have pip 1.3.1 installed
|
||||||
|
self.assertEqual(
|
||||||
|
self.run_function('pip.list', ['pip'], bin_env=venv_dir),
|
||||||
|
{'pip': '1.3.1'}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Now the actual pip upgrade pip test
|
||||||
|
ret = self.run_state(
|
||||||
|
'pip.installed', name='pip==1.4.1', upgrade=True,
|
||||||
|
bin_env=venv_dir
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
self.assertSaltTrueReturn(ret)
|
||||||
|
self.assertInSaltReturn(
|
||||||
|
'Installed',
|
||||||
|
ret,
|
||||||
|
['changes', 'pip==1.4.1']
|
||||||
|
)
|
||||||
|
except AssertionError:
|
||||||
|
import pprint
|
||||||
|
pprint.pprint(ret)
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
if os.path.isdir(venv_dir):
|
||||||
|
shutil.rmtree(venv_dir)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
from integration import run_tests
|
from integration import run_tests
|
||||||
|
@ -783,14 +783,14 @@ class PipTestCase(TestCase):
|
|||||||
'pycrypto==2.6'
|
'pycrypto==2.6'
|
||||||
]
|
]
|
||||||
mock = MagicMock(
|
mock = MagicMock(
|
||||||
return_value={
|
side_effect=[
|
||||||
'retcode': 0,
|
{'retcode': 0, 'stdout': 'pip MOCKED_VERSION'},
|
||||||
'stdout': '\n'.join(eggs)
|
{'retcode': 0, 'stdout': '\n'.join(eggs)}
|
||||||
}
|
]
|
||||||
)
|
)
|
||||||
with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
|
with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
|
||||||
ret = pip.list_()
|
ret = pip.list_()
|
||||||
mock.assert_called_once_with(
|
mock.assert_called_with(
|
||||||
'pip freeze',
|
'pip freeze',
|
||||||
runas=None,
|
runas=None,
|
||||||
cwd=None
|
cwd=None
|
||||||
@ -801,6 +801,7 @@ class PipTestCase(TestCase):
|
|||||||
'M2Crypto': '0.21.1',
|
'M2Crypto': '0.21.1',
|
||||||
'bbfreeze-loader': '1.1.0',
|
'bbfreeze-loader': '1.1.0',
|
||||||
'bbfreeze': '1.1.0',
|
'bbfreeze': '1.1.0',
|
||||||
|
'pip': 'MOCKED_VERSION',
|
||||||
'pycrypto': '2.6'
|
'pycrypto': '2.6'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -829,7 +830,7 @@ class PipTestCase(TestCase):
|
|||||||
)
|
)
|
||||||
with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
|
with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
|
||||||
ret = pip.list_(prefix='bb')
|
ret = pip.list_(prefix='bb')
|
||||||
mock.assert_called_once_with(
|
mock.assert_called_with(
|
||||||
'pip freeze',
|
'pip freeze',
|
||||||
runas=None,
|
runas=None,
|
||||||
cwd=None
|
cwd=None
|
||||||
@ -923,7 +924,7 @@ class PipTestCase(TestCase):
|
|||||||
warnings.resetwarnings()
|
warnings.resetwarnings()
|
||||||
warnings.filterwarnings('always', '', DeprecationWarning, __name__)
|
warnings.filterwarnings('always', '', DeprecationWarning, __name__)
|
||||||
|
|
||||||
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
|
mock = MagicMock(return_value={'retcode': 0, 'stdout': 'pip VERSION'})
|
||||||
with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
|
with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
|
||||||
with warnings.catch_warnings(record=True) as w:
|
with warnings.catch_warnings(record=True) as w:
|
||||||
pip.list_('blah', runas='me!')
|
pip.list_('blah', runas='me!')
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
tests.unit.utils.warnings_test
|
tests.unit.utils.warnings_test
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Test ``salt.utils.warn_until``
|
Test ``salt.utils.warn_until`` and ``salt.utils.kwargs_warn_until``
|
||||||
|
|
||||||
:codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)`
|
:codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)`
|
||||||
:copyright: © 2013 by the SaltStack Team, see AUTHORS for more details.
|
:copyright: © 2013 by the SaltStack Team, see AUTHORS for more details.
|
||||||
@ -20,13 +20,13 @@ from salttesting.mock import NO_MOCK, NO_MOCK_REASON, patch
|
|||||||
ensure_in_syspath('../../')
|
ensure_in_syspath('../../')
|
||||||
|
|
||||||
# Import salt libs
|
# Import salt libs
|
||||||
from salt.utils import warn_until
|
from salt.utils import warn_until, kwargs_warn_until
|
||||||
|
|
||||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
class WarnUntilTestCase(TestCase):
|
class WarnUntilTestCase(TestCase):
|
||||||
|
|
||||||
@patch('salt.version')
|
@patch('salt.version')
|
||||||
def test_warning_raised(self, salt_version_mock):
|
def test_warn_until_warning_raised(self, salt_version_mock):
|
||||||
# We *always* want *all* warnings thrown on this module
|
# We *always* want *all* warnings thrown on this module
|
||||||
warnings.filterwarnings('always', '', DeprecationWarning, __name__)
|
warnings.filterwarnings('always', '', DeprecationWarning, __name__)
|
||||||
|
|
||||||
@ -75,6 +75,54 @@ class WarnUntilTestCase(TestCase):
|
|||||||
(0, 17), 'Foo', _dont_call_warnings=True
|
(0, 17), 'Foo', _dont_call_warnings=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@patch('salt.version')
|
||||||
|
def test_kwargs_warn_until_warning_raised(self, salt_version_mock):
|
||||||
|
# We *always* want *all* warnings thrown on this module
|
||||||
|
warnings.filterwarnings('always', '', DeprecationWarning, __name__)
|
||||||
|
|
||||||
|
# Define a salt version info
|
||||||
|
salt_version_mock.__version_info__ = (0, 16)
|
||||||
|
|
||||||
|
def raise_warning(**kwargs):
|
||||||
|
kwargs_warn_until(
|
||||||
|
kwargs,
|
||||||
|
(0, 17),
|
||||||
|
)
|
||||||
|
|
||||||
|
# raise_warning({...}) should show warning until version info is >= (0, 17)
|
||||||
|
with warnings.catch_warnings(record=True) as recorded_warnings:
|
||||||
|
raise_warning(foo=42) # with a kwarg
|
||||||
|
self.assertEqual(
|
||||||
|
'The following parameter(s) have been deprecated and '
|
||||||
|
'will be removed in 0.17: \'foo\'.',
|
||||||
|
str(recorded_warnings[0].message)
|
||||||
|
)
|
||||||
|
# With no **kwargs, should not show warning until version info is >= (0, 17)
|
||||||
|
with warnings.catch_warnings(record=True) as recorded_warnings:
|
||||||
|
kwargs_warn_until(
|
||||||
|
{}, # no kwargs
|
||||||
|
(0, 17),
|
||||||
|
)
|
||||||
|
self.assertEqual(0, len(recorded_warnings))
|
||||||
|
|
||||||
|
# Let's set version info to (0, 17), a RuntimeError should be raised
|
||||||
|
# regardless of whether or not we pass any **kwargs.
|
||||||
|
salt_version_mock.__version_info__ = (0, 17)
|
||||||
|
with self.assertRaisesRegexp(
|
||||||
|
RuntimeError,
|
||||||
|
r'The warning triggered on filename \'(.*)warnings_test.py\', '
|
||||||
|
r'line number ([\d]+), is supposed to be shown until version '
|
||||||
|
r'\'0.17\' is released. Current version is now \'0.17\'. Please '
|
||||||
|
r'remove the warning.'):
|
||||||
|
raise_warning() # no kwargs
|
||||||
|
with self.assertRaisesRegexp(
|
||||||
|
RuntimeError,
|
||||||
|
r'The warning triggered on filename \'(.*)warnings_test.py\', '
|
||||||
|
r'line number ([\d]+), is supposed to be shown until version '
|
||||||
|
r'\'0.17\' is released. Current version is now \'0.17\'. Please '
|
||||||
|
r'remove the warning.'):
|
||||||
|
raise_warning(bar='baz', qux='quux') # some kwargs
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
from integration import run_tests
|
from integration import run_tests
|
||||||
|
Loading…
Reference in New Issue
Block a user