%PDF- %PDF-
Direktori : /lib/python2.7/site-packages/salt/runners/ |
Current File : //lib/python2.7/site-packages/salt/runners/lxc.py |
# -*- coding: utf-8 -*- ''' Control Linux Containers via Salt :depends: lxc execution module ''' # Import python libs from __future__ import absolute_import, print_function, unicode_literals import time import os import copy import logging # Import Salt libs import salt.client import salt.utils.args import salt.utils.cloud import salt.utils.files import salt.utils.stringutils import salt.utils.virt import salt.key from salt.utils.odict import OrderedDict as _OrderedDict # Import 3rd-party lib from salt.ext import six log = logging.getLogger(__name__) # Don't shadow built-in's. __func_alias__ = { 'list_': 'list' } def _do(name, fun, path=None): ''' Invoke a function in the lxc module with no args path path to the container parent default: /var/lib/lxc (system default) .. versionadded:: 2015.8.0 ''' host = find_guest(name, quiet=True, path=path) if not host: return False client = salt.client.get_local_client(__opts__['conf_file']) cmd_ret = client.cmd_iter( host, 'lxc.{0}'.format(fun), [name], kwarg={'path': path}, timeout=60) data = next(cmd_ret) data = data.get(host, {}).get('ret', None) if data: data = {host: data} return data def _do_names(names, fun, path=None): ''' Invoke a function in the lxc module with no args path path to the container parent default: /var/lib/lxc (system default) .. versionadded:: 2015.8.0 ''' ret = {} hosts = find_guests(names, path=path) if not hosts: return False client = salt.client.get_local_client(__opts__['conf_file']) for host, sub_names in six.iteritems(hosts): cmds = [] for name in sub_names: cmds.append(client.cmd_iter( host, 'lxc.{0}'.format(fun), [name], kwarg={'path': path}, timeout=60)) for cmd in cmds: data = next(cmd) data = data.get(host, {}).get('ret', None) if data: ret.update({host: data}) return ret def find_guest(name, quiet=False, path=None): ''' Returns the host for a container. path path to the container parent default: /var/lib/lxc (system default) .. versionadded:: 2015.8.0 .. code-block:: bash salt-run lxc.find_guest name ''' if quiet: log.warning("'quiet' argument is being deprecated." ' Please migrate to --quiet') for data in _list_iter(path=path): host, l = next(six.iteritems(data)) for x in 'running', 'frozen', 'stopped': if name in l[x]: if not quiet: __jid_event__.fire_event( {'data': host, 'outputter': 'lxc_find_host'}, 'progress') return host return None def find_guests(names, path=None): ''' Return a dict of hosts and named guests path path to the container parent default: /var/lib/lxc (system default) .. versionadded:: 2015.8.0 ''' ret = {} names = names.split(',') for data in _list_iter(path=path): host, stat = next(six.iteritems(data)) for state in stat: for name in stat[state]: if name in names: if host in ret: ret[host].append(name) else: ret[host] = [name] return ret def init(names, host=None, saltcloud_mode=False, quiet=False, **kwargs): ''' Initialize a new container .. code-block:: bash salt-run lxc.init name host=minion_id [cpuset=cgroups_cpuset] \\ [cpushare=cgroups_cpushare] [memory=cgroups_memory] \\ [template=lxc_template_name] [clone=original name] \\ [profile=lxc_profile] [network_proflile=network_profile] \\ [nic=network_profile] [nic_opts=nic_opts] \\ [start=(true|false)] [seed=(true|false)] \\ [install=(true|false)] [config=minion_config] \\ [snapshot=(true|false)] names Name of the containers, supports a single name or a comma delimited list of names. host Minion on which to initialize the container **(required)** path path to the container parent default: /var/lib/lxc (system default) .. versionadded:: 2015.8.0 saltcloud_mode init the container with the saltcloud opts format instead See lxc.init_interface module documentation cpuset cgroups cpuset. cpushare cgroups cpu shares. memory cgroups memory limit, in MB .. versionchanged:: 2015.5.0 If no value is passed, no limit is set. In earlier Salt versions, not passing this value causes a 1024MB memory limit to be set, and it was necessary to pass ``memory=0`` to set no limit. template Name of LXC template on which to base this container clone Clone this container from an existing container profile A LXC profile (defined in config or pillar). network_profile Network profile to use for the container .. versionadded:: 2015.5.2 nic .. deprecated:: 2015.5.0 Use ``network_profile`` instead nic_opts Extra options for network interfaces. E.g.: ``{"eth0": {"mac": "aa:bb:cc:dd:ee:ff", "ipv4": "10.1.1.1", "ipv6": "2001:db8::ff00:42:8329"}}`` start Start the newly created container. seed Seed the container with the minion config and autosign its key. Default: true install If salt-minion is not already installed, install it. Default: true config Optional config parameters. By default, the id is set to the name of the container. ''' path = kwargs.get('path', None) if quiet: log.warning("'quiet' argument is being deprecated." ' Please migrate to --quiet') ret = {'comment': '', 'result': True} if host is None: # TODO: Support selection of host based on available memory/cpu/etc. ret['comment'] = 'A host must be provided' ret['result'] = False return ret if isinstance(names, six.string_types): names = names.split(',') if not isinstance(names, list): ret['comment'] = 'Container names are not formed as a list' ret['result'] = False return ret # check that the host is alive client = salt.client.get_local_client(__opts__['conf_file']) alive = False try: if client.cmd(host, 'test.ping', timeout=20).get(host, None): alive = True except (TypeError, KeyError): pass if not alive: ret['comment'] = 'Host {0} is not reachable'.format(host) ret['result'] = False return ret log.info('Searching for LXC Hosts') data = __salt__['lxc.list'](host, quiet=True, path=path) for host, containers in six.iteritems(data): for name in names: if name in sum(six.itervalues(containers), []): log.info( 'Container \'%s\' already exists on host \'%s\', init ' 'can be a NO-OP', name, host ) if host not in data: ret['comment'] = 'Host \'{0}\' was not found'.format(host) ret['result'] = False return ret kw = salt.utils.args.clean_kwargs(**kwargs) pub_key = kw.get('pub_key', None) priv_key = kw.get('priv_key', None) explicit_auth = pub_key and priv_key approve_key = kw.get('approve_key', True) seeds = {} seed_arg = kwargs.get('seed', True) if approve_key and not explicit_auth: skey = salt.key.Key(__opts__) all_minions = skey.all_keys().get('minions', []) for name in names: seed = seed_arg if name in all_minions: try: if client.cmd(name, 'test.ping', timeout=20).get(name, None): seed = False except (TypeError, KeyError): pass seeds[name] = seed kv = salt.utils.virt.VirtKey(host, name, __opts__) if kv.authorize(): log.info('Container key will be preauthorized') else: ret['comment'] = 'Container key preauthorization failed' ret['result'] = False return ret log.info('Creating container(s) \'%s\' on host \'%s\'', names, host) cmds = [] for name in names: args = [name] kw = salt.utils.args.clean_kwargs(**kwargs) if saltcloud_mode: kw = copy.deepcopy(kw) kw['name'] = name saved_kwargs = kw kw = client.cmd( host, 'lxc.cloud_init_interface', args + [kw], tgt_type='list', timeout=600).get(host, {}) kw.update(saved_kwargs) name = kw.pop('name', name) # be sure not to seed an already seeded host kw['seed'] = seeds.get(name, seed_arg) if not kw['seed']: kw.pop('seed_cmd', '') cmds.append( (host, name, client.cmd_iter(host, 'lxc.init', args, kwarg=kw, timeout=600))) done = ret.setdefault('done', []) errors = ret.setdefault('errors', _OrderedDict()) for ix, acmd in enumerate(cmds): hst, container_name, cmd = acmd containers = ret.setdefault(hst, []) herrs = errors.setdefault(hst, _OrderedDict()) serrs = herrs.setdefault(container_name, []) sub_ret = next(cmd) error = None if isinstance(sub_ret, dict) and host in sub_ret: j_ret = sub_ret[hst] container = j_ret.get('ret', {}) if container and isinstance(container, dict): if not container.get('result', False): error = container else: error = 'Invalid return for {0}: {1} {2}'.format( container_name, container, sub_ret) else: error = sub_ret if not error: error = 'unknown error (no return)' if error: ret['result'] = False serrs.append(error) else: container['container_name'] = name containers.append(container) done.append(container) # marking ping status as True only and only if we have at # least provisioned one container ret['ping_status'] = bool(len(done)) # for all provisioned containers, last job is to verify # - the key status # - we can reach them for container in done: # explicitly check and update # the minion key/pair stored on the master container_name = container['container_name'] key = os.path.join(__opts__['pki_dir'], 'minions', container_name) if explicit_auth: fcontent = '' if os.path.exists(key): with salt.utils.files.fopen(key) as fic: fcontent = salt.utils.stringutils.to_unicode(fic.read()).strip() pub_key = salt.utils.stringutils.to_unicode(pub_key) if pub_key.strip() != fcontent: with salt.utils.files.fopen(key, 'w') as fic: fic.write(salt.utils.stringutils.to_str(pub_key)) fic.flush() mid = j_ret.get('mid', None) if not mid: continue def testping(**kw): mid_ = kw['mid'] ping = client.cmd(mid_, 'test.ping', timeout=20) time.sleep(1) if ping: return 'OK' raise Exception('Unresponsive {0}'.format(mid_)) ping = salt.utils.cloud.wait_for_fun(testping, timeout=21, mid=mid) if ping != 'OK': ret['ping_status'] = False ret['result'] = False # if no lxc detected as touched (either inited or verified) # we result to False if not done: ret['result'] = False if not quiet: __jid_event__.fire_event({'message': ret}, 'progress') return ret def cloud_init(names, host=None, quiet=False, **kwargs): ''' Wrapper for using lxc.init in saltcloud compatibility mode names Name of the containers, supports a single name or a comma delimited list of names. host Minion to start the container on. Required. path path to the container parent default: /var/lib/lxc (system default) .. versionadded:: 2015.8.0 saltcloud_mode init the container with the saltcloud opts format instead ''' if quiet: log.warning("'quiet' argument is being deprecated. Please migrate to --quiet") return __salt__['lxc.init'](names=names, host=host, saltcloud_mode=True, quiet=quiet, **kwargs) def _list_iter(host=None, path=None): ''' Return a generator iterating over hosts path path to the container parent default: /var/lib/lxc (system default) .. versionadded:: 2015.8.0 ''' tgt = host or '*' client = salt.client.get_local_client(__opts__['conf_file']) for container_info in client.cmd_iter( tgt, 'lxc.list', kwarg={'path': path} ): if not container_info: continue if not isinstance(container_info, dict): continue chunk = {} id_ = next(six.iterkeys(container_info)) if host and host != id_: continue if not isinstance(container_info[id_], dict): continue if 'ret' not in container_info[id_]: continue if not isinstance(container_info[id_]['ret'], dict): continue chunk[id_] = container_info[id_]['ret'] yield chunk def list_(host=None, quiet=False, path=None): ''' List defined containers (running, stopped, and frozen) for the named (or all) host(s). path path to the container parent default: /var/lib/lxc (system default) .. versionadded:: 2015.8.0 .. code-block:: bash salt-run lxc.list [host=minion_id] ''' it = _list_iter(host, path=path) ret = {} for chunk in it: ret.update(chunk) if not quiet: __jid_event__.fire_event( {'data': chunk, 'outputter': 'lxc_list'}, 'progress') return ret def purge(name, delete_key=True, quiet=False, path=None): ''' Purge the named container and delete its minion key if present. WARNING: Destroys all data associated with the container. path path to the container parent default: /var/lib/lxc (system default) .. versionadded:: 2015.8.0 .. code-block:: bash salt-run lxc.purge name ''' data = _do_names(name, 'destroy', path=path) if data is False: return data if delete_key: skey = salt.key.Key(__opts__) skey.delete_key(name) if data is None: return if not quiet: __jid_event__.fire_event( {'data': data, 'outputter': 'lxc_purge'}, 'progress') return data def start(name, quiet=False, path=None): ''' Start the named container. path path to the container parent default: /var/lib/lxc (system default) .. versionadded:: 2015.8.0 .. code-block:: bash salt-run lxc.start name ''' data = _do_names(name, 'start', path=path) if data and not quiet: __jid_event__.fire_event( {'data': data, 'outputter': 'lxc_start'}, 'progress') return data def stop(name, quiet=False, path=None): ''' Stop the named container. path path to the container parent default: /var/lib/lxc (system default) .. versionadded:: 2015.8.0 .. code-block:: bash salt-run lxc.stop name ''' data = _do_names(name, 'stop', path=path) if data and not quiet: __jid_event__.fire_event( {'data': data, 'outputter': 'lxc_force_off'}, 'progress') return data def freeze(name, quiet=False, path=None): ''' Freeze the named container path path to the container parent default: /var/lib/lxc (system default) .. versionadded:: 2015.8.0 .. code-block:: bash salt-run lxc.freeze name ''' data = _do_names(name, 'freeze') if data and not quiet: __jid_event__.fire_event( {'data': data, 'outputter': 'lxc_pause'}, 'progress') return data def unfreeze(name, quiet=False, path=None): ''' Unfreeze the named container path path to the container parent default: /var/lib/lxc (system default) .. versionadded:: 2015.8.0 .. code-block:: bash salt-run lxc.unfreeze name ''' data = _do_names(name, 'unfreeze', path=path) if data and not quiet: __jid_event__.fire_event( {'data': data, 'outputter': 'lxc_resume'}, 'progress') return data def info(name, quiet=False, path=None): ''' Returns information about a container. path path to the container parent default: /var/lib/lxc (system default) .. versionadded:: 2015.8.0 .. code-block:: bash salt-run lxc.info name ''' data = _do_names(name, 'info', path=path) if data and not quiet: __jid_event__.fire_event( {'data': data, 'outputter': 'lxc_info'}, 'progress') return data