Merge pull request #23898 from makinacorpus/lxc_profiles

Lxc profiles
This commit is contained in:
Colton Myers 2015-05-19 15:08:28 -06:00
commit 5bdbf0af9b
3 changed files with 456 additions and 78 deletions

View file

@ -10,7 +10,7 @@ and possibly remote minion.
In other words, Salt will connect to a minion, then from that minion:
- Provision and configure a container for networking access
- Use :ref:`saltify <config_saltify>` to deploy salt and re-attach to master
- Use **lxc** and **config** module to deploy salt and re-attach to master
Limitations
------------
@ -61,7 +61,18 @@ Here are the options to configure your containers:
``target``
Host minion id to install the lxc Container into
``profile``
Name of the profile containing the LXC configuration
Name of the profile containing the LXC configuration defaults
``network_profile``
Name of the profile containing the LXC network configuration defaults
``nic_opts``
Per interface new-style configuration options mappings::
eth0: {'mac': '00:16:3e:01:29:40',
'gateway': None, (default)
'link': 'br0', (default)
'gateway': None, (default)
'netmask': '', (default)
'ip': '22.1.4.25'}}
Container creation/clone options:
Create a container by cloning:
@ -141,4 +152,4 @@ Driver Support
- Container creation
- Image listing (LXC templates)
- Running container information (IP addresses, etc.)
- Running container information (IP addresses, etc.)

View file

@ -225,6 +225,10 @@ def _salt(fun, *args, **kw):
if target in cret:
ret = cret[target]
break
# recent changes
elif 'data' in cret and 'outputter' in cret:
ret = cret['data']
break
# special case, some answers may be crafted
# to handle the unresponsivness of a specific command
# which is also meaningful, e.g. a minion not yet provisioned

View file

@ -13,6 +13,7 @@ from __future__ import absolute_import, print_function
import datetime
import pipes
import copy
import textwrap
import difflib
import logging
import tempfile
@ -49,6 +50,7 @@ __func_alias__ = {
}
DEFAULT_NIC = 'eth0'
DEFAULT_BR = 'br0'
SEED_MARKER = '/lxc.initial_seed'
PATH = 'PATH=/bin:/usr/bin:/sbin:/usr/sbin:/opt/bin:' \
'/usr/local/bin:/usr/local/sbin'
@ -101,6 +103,92 @@ def _ip_sort(ip):
return '{0}___{1}'.format(idx, ip)
def search_lxc_bridges():
'''
Search which bridges are potentially available as LXC bridges
CLI Example:
.. code-block:: bash
salt '*' lxc.search_lxc_bridges
'''
bridges = __context__.get('lxc.bridges', None)
# either match not yet called or no bridges were found
# to handle the case where lxc was not installed on the first
# call
if not bridges:
bridges = set()
running_bridges = set()
bridges.add(DEFAULT_BR)
try:
output = __salt__['cmd.run_all']('brctl show')
for line in output['stdout'].splitlines()[1:]:
if not line.startswith(' '):
running_bridges.add(line.split()[0].strip())
except (SaltInvocationError, CommandExecutionError):
pass
for ifc, ip in six.iteritems(
__grains__.get('ip_interfaces', {})
):
if ifc in running_bridges:
bridges.add(ifc)
elif os.path.exists(
'a/sys/devices/virtual/net/{0}/bridge'.format(ifc)
):
bridges.add(ifc)
bridges = list(bridges)
# if we found interfaces that have lxc in their names
# we filter them as being the potential lxc bridges
# we also try to default on br0 on other cases
def sort_bridges(a):
pref = 'z'
if 'lxc' in a:
pref = 'a'
elif 'br0' == a:
pref = 'c'
return '{0}_{1}'.format(pref, a)
bridges.sort(key=sort_bridges)
__context__['lxc.bridges'] = bridges
return bridges
def search_lxc_bridge():
'''
Search the first bridge which is potentially available as LXC bridge
CLI Example:
.. code-block:: bash
salt '*' lxc.search_lxc_bridge
'''
return search_lxc_bridges()[0]
def _get_salt_config(config, **kwargs):
if not config:
config = kwargs.get('minion', {})
if not config:
config = {}
config.setdefault('master',
kwargs.get('master',
__opts__.get('master',
__opts__['id'])))
config.setdefault(
'master_port',
kwargs.get('master_port',
__opts__.get('master_port',
__opts__.get('ret_port',
__opts__.get('4506')))))
if not config['master']:
config = {}
return config
def cloud_init_interface(name, vm_=None, **kwargs):
'''
Interface between salt.cloud.lxc driver and lxc.init
@ -148,6 +236,41 @@ def cloud_init_interface(name, vm_=None, **kwargs):
priv_key
private key to preseed the minion with.
Can be the keycontent or a filepath
network_profile
salt lxc network profile selection
nic_opts
per interface settings compatibles with
network profile (ipv4/ipv6/link/gateway/mac/netmask)
eg::
- {'eth0': {'mac': '00:16:3e:01:29:40',
'gateway': None, (default)
'link': 'br0', (default)
'gateway': None, (default)
'netmask': '', (default)
'ip': '22.1.4.25'}}
unconditional_install
given to lxc.bootstrap (see relative doc)
force_install
given to lxc.bootstrap (see relative doc)
config
any extra argument for the salt minion config
dnsservers
dns servers to set inside the container
autostart
autostart the container at boot time
password
administrative password for the container
users
administrative users for the container
default: [root] and [root, ubuntu] on ubuntu
default_nic
name of the first interface, you should
really not override this
Legacy but still supported options:
ip
ip for the primary nic
mac
@ -174,21 +297,6 @@ def cloud_init_interface(name, vm_=None, **kwargs):
'link': 'br0', (default)
'netmask': '', (default)
'ip': '22.1.4.25'}
unconditional_install
given to lxc.bootstrap (see relative doc)
force_install
given to lxc.bootstrap (see relative doc)
config
any extra argument for the salt minion config
dnsservers
dns servers to set inside the container
autostart
autostart the container at boot time
password
administrative password for the container
users
administrative users for the container
default: [root] and [root, ubuntu] on ubuntu
CLI Example:
@ -220,8 +328,14 @@ def cloud_init_interface(name, vm_=None, **kwargs):
default_template = 'ubuntu'
image = vm_.get('image', profile.get('template',
default_template))
vgname = vm_.get('vgname', None)
backing = vm_.get('backing', 'dir')
if image:
profile['template'] = image
vgname = vm_.get('vgname', None)
if vgname:
profile['vgname'] = vgname
if backing:
profile['backing'] = backing
snapshot = vm_.get('snapshot', False)
autostart = bool(vm_.get('autostart', True))
dnsservers = vm_.get('dnsservers', [])
@ -236,65 +350,65 @@ def cloud_init_interface(name, vm_=None, **kwargs):
size = vm_.get('size', '20G')
script = vm_.get('script', None)
script_args = vm_.get('script_args', None)
if image:
profile['template'] = image
if vgname:
profile['vgname'] = vgname
if backing:
profile['backing'] = backing
users = vm_.get('users', None)
if users is None:
users = []
ssh_username = vm_.get('ssh_username', None)
if ssh_username and (ssh_username not in users):
users.append(ssh_username)
ip = vm_.get('ip', None)
mac = vm_.get('mac', None)
net_profile = vm_.get('network_profile', _marker)
nic_opts = kwargs.pop('nic_opts', None)
netmask = vm_.get('netmask', '24')
bridge = vm_.get('bridge', 'lxcbr0')
gateway = vm_.get('gateway', 'auto')
bridge = vm_.get('bridge', None)
gateway = vm_.get('gateway', None)
unconditional_install = vm_.get('unconditional_install', False)
force_install = vm_.get('force_install', True)
config = vm_.get('config', {})
if not config:
config = vm_.get('minion', {})
if not config:
config = {}
config.setdefault('master',
vm_.get('master',
__opts__.get('master',
__opts__['id'])))
config.setdefault(
'master_port',
vm_.get('master_port',
__opts__.get('master_port',
__opts__.get('ret_port',
__opts__.get('4506')))))
if not config['master']:
config = {}
eth0 = {}
nic_opts = {'eth0': eth0}
bridge = vm_.get('bridge', 'lxcbr0')
if ip is None:
nic_opts = None
else:
config = _get_salt_config(vm_.get('config', {}), **vm_)
default_nic = vm_.get('default_nic', DEFAULT_NIC)
# do the interface with lxc.init mainly via nic_opts
# to avoid extra and confusing extra use cases.
if not isinstance(nic_opts, dict):
nic_opts = salt.utils.odict.OrderedDict()
# have a reference to the default nic
eth0 = nic_opts.setdefault(default_nic,
salt.utils.odict.OrderedDict())
# lxc config is based of ifc order, be sure to use odicts.
if not isinstance(nic_opts, salt.utils.odict.OrderedDict):
bnic_opts = salt.utils.odict.OrderedDict()
bnic_opts.update(nic_opts)
nic_opts = bnic_opts
gw = None
# legacy salt.cloud scheme for network interfaces settings support
bridge = vm_.get('bridge', None)
ip = vm_.get('ip', None)
mac = vm_.get('mac', None)
if ip:
fullip = ip
if netmask:
fullip += '/{0}'.format(netmask)
eth0['ipv4'] = fullip
if mac is not None:
eth0['mac'] = mac
if bridge:
eth0['link'] = bridge
for ix, iopts in enumerate(vm_.get("additional_ips", [])):
ifh = "eth{0}".format(ix+1)
ethx = nic_opts.setdefault(ifh, {})
gw = iopts.get('gateway', None)
if gw:
ethx['gateway'] = gw
vm_['gateway'] = gateway = None
ethx['link'] = iopts.get('link', 'br0')
ethx['ipv4'] = iopts['ip']
if gw is None:
gw = iopts.get('gateway', ethx.get('gateway', None))
if gw:
# only one and only one default gateway is allowed !
eth0.pop('gateway', None)
gateway = None
# even if the gateway if on default "eth0" nic
# and we popped it will work
# as we reinject or set it here.
ethx['gateway'] = gw
elink = iopts.get('link', ethx.get('link', None))
if elink:
ethx['link'] = elink
# allow dhcp
aip = iopts.get('ipv4', iopts.get('ip', None))
if aip:
ethx['ipv4'] = aip
nm = iopts.get('netmask', '')
if nm:
ethx['ipv4'] += '/{0}'.format(nm)
@ -303,8 +417,25 @@ def cloud_init_interface(name, vm_=None, **kwargs):
ethx['mac'] = iopts[i]
break
if 'mac' not in ethx:
raise ValueError('No mac for {0}'.format(ifh))
gateway = vm_.get('gateway', 'auto')
ethx['mac'] = salt.utils.gen_mac()
# last round checking for unique gateway and such
gw = None
for ethx in [a for a in nic_opts]:
ndata = nic_opts[ethx]
if gw:
ndata.pop('gateway', None)
if 'gateway' in ndata:
gw = ndata['gateway']
gateway = None
# only use a default bridge / gateway if we configured them
# via the legacy salt cloud configuration style.
# On other cases, we should rely on settings provided by the new
# salt lxc network profile style configuration which can
# be also be overriden or a per interface basis via the nic_opts dict.
if bridge:
eth0['link'] = bridge
if gateway:
eth0['gateway'] = gateway
#
lxc_init_interface = {}
lxc_init_interface['name'] = name
@ -312,8 +443,6 @@ def cloud_init_interface(name, vm_=None, **kwargs):
lxc_init_interface['memory'] = vm_.get('memory', 0) # nolimit
lxc_init_interface['pub_key'] = pub_key
lxc_init_interface['priv_key'] = priv_key
lxc_init_interface['bridge'] = bridge
lxc_init_interface['gateway'] = gateway
lxc_init_interface['nic_opts'] = nic_opts
for clone_from in ['clone_from', 'clone', 'from_container']:
# clone_from should default to None if not available
@ -338,7 +467,10 @@ def cloud_init_interface(name, vm_=None, **kwargs):
lxc_init_interface['users'] = users
lxc_init_interface['password'] = password
lxc_init_interface['password_encrypted'] = password_encrypted
lxc_init_interface['network_profile'] = DEFAULT_NIC
# be sure not to let objects goes inside the return
# as this return will be msgpacked for use in the runner !
if net_profile is not _marker:
lxc_init_interface['network_profile'] = net_profile
for i in ['cpu', 'cpuset', 'cpushare']:
if vm_.get(i, None):
lxc_init_interface[i] = vm_[i]
@ -492,6 +624,9 @@ def get_network_profile(name=None):
'lxc.nic has been deprecated, please configure LXC '
'network profiles under lxc.network_profile instead'
)
if name == DEFAULT_NIC and not net_profile:
net_profile = {DEFAULT_NIC: {}}
return net_profile if net_profile is not None else {}
@ -585,7 +720,7 @@ def _network_conf(conf_tuples=None, **kwargs):
'test': not link,
'value': link,
'old': old_if.get('lxc.network.link'),
'default': 'br0'}),
'default': search_lxc_bridge()}),
('lxc.network.hwaddr', {
'test': not mac,
'value': mac,
@ -1025,6 +1160,12 @@ def init(name,
Optional config parameters. By default, the id is set to
the name of the container.
master
salt master (default to minion's master)
master_port
salt master port (default to minion's master port)
pub_key
Explicit public key to preseed the minion with (optional).
This can be either a filepath or a string representing the key
@ -1143,7 +1284,7 @@ def init(name,
seed = select('seed', True)
install = select('install', True)
seed_cmd = select('seed_cmd')
salt_config = select('config')
salt_config = _get_salt_config(config, **kwargs)
approve_key = select('approve_key', True)
clone_from = select('clone_from')
if password and password_encrypted is None:
@ -1272,6 +1413,7 @@ def init(name,
'/.lxc.{0}.initial_pass'.format(name)]
if not any(retcode(name,
'test -e "{0}"'.format(x),
chroot_fallback=True,
ignore_retcode=True) == 0
for x in gids):
# think to touch the default user generated by default templates
@ -1279,11 +1421,12 @@ def init(name,
# root is defined as a member earlier in the code
for default_user in ['ubuntu']:
if (
default_user not in users
and retcode(name,
'id {0}'.format(default_user),
python_shell=False,
ignore_retcode=True) == 0
default_user not in users and
retcode(name,
'id {0}'.format(default_user),
python_shell=False,
chroot_fallback=True,
ignore_retcode=True) == 0
):
users.append(default_user)
for user in users:
@ -1307,6 +1450,7 @@ def init(name,
if retcode(name,
('sh -c \'touch "{0}"; test -e "{0}"\''
.format(gid)),
chroot_fallback=True,
ignore_retcode=True) != 0:
ret['comment'] = 'Failed to set password marker'
changes[-1]['password'] += '. ' + ret['comment'] + '.'
@ -1321,6 +1465,7 @@ def init(name,
'/lxc.{0}.initial_dns'.format(name)]
if not any(retcode(name,
'test -e "{0}"'.format(x),
chroot_fallback=True,
ignore_retcode=True) == 0
for x in gids):
try:
@ -1335,6 +1480,7 @@ def init(name,
if retcode(name,
('sh -c \'touch "{0}"; test -e "{0}"\''
.format(gid)),
chroot_fallback=True,
ignore_retcode=True) != 0:
ret['comment'] = 'Failed to set DNS marker'
changes[-1]['dns'] += '. ' + ret['comment'] + '.'
@ -1346,9 +1492,9 @@ def init(name,
if (
any(retcode(name,
'test -e {0}'.format(x),
chroot_fallback=True,
ignore_retcode=True) == 0
for x in gids)
or not ret.get('result', True)
for x in gids) or not ret.get('result', True)
):
pass
elif seed or seed_cmd:
@ -2438,6 +2584,7 @@ def set_password(name, users, password, encrypted=True):
'chpasswd{0}'.format(' -e' if encrypted else ''),
stdin=':'.join((user, password)),
python_shell=False,
chroot_fallback=True,
output_loglevel='quiet')
if result != 0:
failed_users.append(user)
@ -2599,6 +2746,7 @@ def set_dns(name, dnsservers=None, searchdomains=None):
script = '/sbin/{0}_dns.sh'.format(rstr)
DNS_SCRIPT = "\n".join([
# 'set -x',
'#!/usr/bin/env bash',
'if [ -h /etc/resolv.conf ];then',
' if [ "x$(readlink /etc/resolv.conf)"'
' = "x../run/resolvconf/resolv.conf" ];then',
@ -2633,7 +2781,183 @@ def set_dns(name, dnsservers=None, searchdomains=None):
return True
def _need_install(name):
def running_systemd(name, cache=True):
'''
Determine if systemD is running
CLI Example:
.. code-block:: bash
salt '*' lxc.running_systemd ubuntu
'''
k = 'lxc.systemd.test.{0}'.format(name)
ret = __context__.get(k, None)
if ret is None or not cache:
rstr = __salt__['test.rand_str']()
# no tmp here, apparmor wont let us execute !
script = '/sbin/{0}_testsystemd.sh'.format(rstr)
# ubuntu already had since trusty some bits of systemd but was
# still using upstart ...
# we need to be a bit more careful that just testing that systemd
# is present
_script = textwrap.dedent(
'''\
#!/usr/bin/env bash
set -x
if ! which systemctl 1>/dev/nulll 2>/dev/null;then exit 2;fi
for i in \\
/run/systemd/journal/dev-log\\
/run/systemd/journal/flushed\\
/run/systemd/journal/kernel-seqnum\\
/run/systemd/journal/socket\\
/run/systemd/journal/stdout\\
/var/run/systemd/journal/dev-log\\
/var/run/systemd/journal/flushed\\
/var/run/systemd/journal/kernel-seqnum\\
/var/run/systemd/journal/socket\\
/var/run/systemd/journal/stdout\\
;do\\
if test -e ${i};then exit 0;fi
done
if test -d /var/systemd/system;then exit 0;fi
exit 2
''')
result = run_all(
name, 'tee {0}'.format(script), stdin=_script, python_shell=True)
if result['retcode'] == 0:
result = run_all(name,
'sh -c "chmod +x {0};{0}"'''.format(script),
python_shell=True)
else:
raise CommandExecutionError(
'lxc {0} failed to copy initd tester'.format(name))
run_all(name,
'sh -c \'if [ -f "{0}" ];then rm -f "{0}";fi\''
''.format(script),
ignore_retcode=True,
python_shell=True)
if result['retcode'] != 0:
error = ('Unable to determine if the container \'{0}\''
' was running systemd, assmuming it is not.'
''.format(name))
if result['stderr']:
error += ': {0}'.format(result['stderr'])
# only cache result if we got a known exit code
if result['retcode'] in (0, 2):
__context__[k] = ret = not result['retcode']
return ret
def systemd_running_state(name):
'''
Get the operational state of a systemd based container
CLI Example:
.. code-block:: bash
salt myminion lxc.systemd_running_state ubuntu
'''
try:
ret = run_all(name,
'systemctl is-system-running',
ignore_retcode=True)['stdout']
except CommandExecutionError:
ret = ''
return ret
def test_sd_started_state(name):
'''
Test if a systemd container is fully started
CLI Example:
.. code-block:: bash
salt myminion lxc.test_sd_started_state ubuntu
'''
qstate = systemd_running_state(name)
if qstate in ('initializing', 'starting'):
return False
elif qstate == '':
return None
else:
return True
def test_bare_started_state(name):
'''
Test if a non systemd container is fully started
For now, it consists only to test if the container is attachable
CLI Example:
.. code-block:: bash
salt myminion lxc.test_bare_started_state ubuntu
'''
try:
ret = run_all(name, 'ls', ignore_retcode=True)['retcode'] == 0
except (CommandExecutionError,):
ret = None
return ret
def wait_started(name, timeout=300):
'''
Check that the system has fully inited
This is actually very important for systemD based containers
see https://github.com/saltstack/salt/issues/23847
CLI Example:
.. code-block:: bash
salt myminion lxc.wait_started ubuntu
'''
if not exists(name):
raise CommandExecutionError(
'Container {0} does does exists'.format(name))
if not state(name) == 'running':
raise CommandExecutionError(
'Container {0} is not running'.format(name))
ret = False
if running_systemd(name):
test_started = test_sd_started_state
logger = log.error
else:
test_started = test_bare_started_state
logger = log.debug
now = time.time()
expire = now + timeout
now = time.time()
started = test_started(name)
while time.time() < expire and not started:
time.sleep(0.3)
started = test_started(name)
if started is None:
logger(
'Assuming {0} is started, although we failed to detect that'
' is fully started correctly'.format(name))
ret = True
else:
ret = started
return ret
def _needs_install(name):
ret = 0
has_minion = retcode(name,
'which salt-minion',
@ -2718,6 +3042,7 @@ def bootstrap(name,
[approve_key=(True|False)] [install=(True|False)]
'''
wait_started(name)
if bootstrap_delay is not None:
try:
time.sleep(bootstrap_delay)
@ -2747,11 +3072,12 @@ def bootstrap(name,
if not orig_state:
return orig_state
if not force_install:
needs_install = _need_install(name)
needs_install = _needs_install(name)
else:
needs_install = True
seeded = retcode(name,
'test -e \'{0}\''.format(SEED_MARKER),
chroot_fallback=True,
ignore_retcode=True) == 0
tmp = tempfile.mkdtemp()
if seeded and not unconditional_install:
@ -2862,6 +3188,7 @@ def _run(name,
output_loglevel='debug',
use_vt=False,
ignore_retcode=False,
chroot_fallback=None,
keep_env='http_proxy,https_proxy,no_proxy'):
'''
Common logic for lxc.run functions
@ -2897,6 +3224,9 @@ def _run(name,
ignore_retcode=ignore_retcode,
use_vt=use_vt)
else:
if not chroot_fallback:
raise CommandExecutionError(
'{0} is not attachable.'.format(name))
rootfs = info(name).get('rootfs')
# Set context var to make cmd.run_chroot run cmd.run instead of
# cmd.run_all.
@ -2935,6 +3265,7 @@ def run_cmd(name,
output_loglevel='debug',
use_vt=False,
ignore_retcode=False,
chroot_fallback=False,
keep_env='http_proxy,https_proxy,no_proxy'):
'''
.. deprecated:: 2015.5.0
@ -2976,6 +3307,7 @@ def run(name,
output_loglevel='debug',
use_vt=False,
ignore_retcode=False,
chroot_fallback=False,
keep_env='http_proxy,https_proxy,no_proxy'):
'''
.. versionadded:: 2015.5.0
@ -3019,6 +3351,10 @@ def run(name,
Use SaltStack's utils.vt to stream output to console. Assumes
``output=all``.
chroot_fallback
if the container is not running, try to run the command using chroot
default: false
keep_env : http_proxy,https_proxy,no_proxy
A list of env vars to preserve. May be passed as commma-delimited list.
@ -3039,6 +3375,7 @@ def run(name,
output_loglevel=output_loglevel,
use_vt=use_vt,
ignore_retcode=ignore_retcode,
chroot_fallback=chroot_fallback,
keep_env=keep_env)
@ -3051,6 +3388,7 @@ def run_stdout(name,
output_loglevel='debug',
use_vt=False,
ignore_retcode=False,
chroot_fallback=False,
keep_env='http_proxy,https_proxy,no_proxy'):
'''
.. versionadded:: 2015.5.0
@ -3097,6 +3435,10 @@ def run_stdout(name,
keep_env : http_proxy,https_proxy,no_proxy
A list of env vars to preserve. May be passed as commma-delimited list.
chroot_fallback
if the container is not running, try to run the command using chroot
default: false
CLI Example:
@ -3114,6 +3456,7 @@ def run_stdout(name,
output_loglevel=output_loglevel,
use_vt=use_vt,
ignore_retcode=ignore_retcode,
chroot_fallback=chroot_fallback,
keep_env=keep_env)
@ -3126,6 +3469,7 @@ def run_stderr(name,
output_loglevel='debug',
use_vt=False,
ignore_retcode=False,
chroot_fallback=False,
keep_env='http_proxy,https_proxy,no_proxy'):
'''
.. versionadded:: 2015.5.0
@ -3170,6 +3514,10 @@ def run_stderr(name,
keep_env : http_proxy,https_proxy,no_proxy
A list of env vars to preserve. May be passed as commma-delimited list.
chroot_fallback
if the container is not running, try to run the command using chroot
default: false
CLI Example:
@ -3187,6 +3535,7 @@ def run_stderr(name,
output_loglevel=output_loglevel,
use_vt=use_vt,
ignore_retcode=ignore_retcode,
chroot_fallback=chroot_fallback,
keep_env=keep_env)
@ -3199,6 +3548,7 @@ def retcode(name,
output_loglevel='debug',
use_vt=False,
ignore_retcode=False,
chroot_fallback=False,
keep_env='http_proxy,https_proxy,no_proxy'):
'''
.. versionadded:: 2015.5.0
@ -3245,6 +3595,10 @@ def retcode(name,
keep_env : http_proxy,https_proxy,no_proxy
A list of env vars to preserve. May be passed as commma-delimited list.
chroot_fallback
if the container is not running, try to run the command using chroot
default: false
CLI Example:
@ -3262,6 +3616,7 @@ def retcode(name,
output_loglevel=output_loglevel,
use_vt=use_vt,
ignore_retcode=ignore_retcode,
chroot_fallback=chroot_fallback,
keep_env=keep_env)
@ -3274,6 +3629,7 @@ def run_all(name,
output_loglevel='debug',
use_vt=False,
ignore_retcode=False,
chroot_fallback=False,
keep_env='http_proxy,https_proxy,no_proxy'):
'''
.. versionadded:: 2015.5.0
@ -3318,6 +3674,10 @@ def run_all(name,
keep_env : http_proxy,https_proxy,no_proxy
A list of env vars to preserve. May be passed as commma-delimited list.
chroot_fallback
if the container is not running, try to run the command using chroot
default: false
CLI Example:
@ -3335,6 +3695,7 @@ def run_all(name,
output_loglevel=output_loglevel,
use_vt=use_vt,
ignore_retcode=ignore_retcode,
chroot_fallback=chroot_fallback,
keep_env=keep_env)
@ -3342,7 +3703,9 @@ def _get_md5(name, path):
'''
Get the MD5 checksum of a file from a container
'''
output = run_stdout(name, 'md5sum "{0}"'.format(path), ignore_retcode=True)
output = run_stdout(name, 'md5sum "{0}"'.format(path),
chroot_fallback=True,
ignore_retcode=True)
try:
return output.split()[0]
except IndexError:
@ -3626,7 +3989,7 @@ def edit_conf(conf_file, out_format='simple', read_only=False, lxc_config=None,
net_config.append(net_params)
nic_opts = salt.utils.odict.OrderedDict()
for params in net_config:
dev = params.get('lxc.network.name', 'eth0')
dev = params.get('lxc.network.name', DEFAULT_NIC)
dev_opts = nic_opts.setdefault(dev, salt.utils.odict.OrderedDict())
for param in params:
opt = param.replace('lxc.network.', '')