Browse Source

added seedng

tags/2016.12^2
Ondrej Smola 8 years ago
parent
commit
86bf61a9d7
2 changed files with 299 additions and 1 deletions
  1. +298
    -0
      _modules/seedng.py
  2. +1
    -1
      _modules/virtng.py

+ 298
- 0
_modules/seedng.py View File

@@ -0,0 +1,298 @@
# -*- coding: utf-8 -*-
'''
Virtual machine image management tools
'''

from __future__ import absolute_import

# Import python libs
import os
import shutil
import logging
import tempfile

# Import salt libs
import salt.crypt
import salt.utils
import salt.utils.cloud
import salt.config
import salt.syspaths
import uuid


# Set up logging
log = logging.getLogger(__name__)

# Don't shadow built-in's.
__func_alias__ = {
'apply_': 'apply'
}


def _file_or_content(file_):
if os.path.exists(file_):
with salt.utils.fopen(file_) as fic:
return fic.read()
return file_


def prep_bootstrap(mpt):
'''
Update and get the random script to a random place

CLI Example:

.. code-block:: bash

salt '*' seed.prep_bootstrap /tmp

'''
# Verify that the boostrap script is downloaded
bs_ = __salt__['config.gather_bootstrap_script']()
fpd_ = os.path.join(mpt, 'tmp', "{0}".format(
uuid.uuid4()))
if not os.path.exists(fpd_):
os.makedirs(fpd_)
os.chmod(fpd_, 0o700)
fp_ = os.path.join(fpd_, os.path.basename(bs_))
# Copy script into tmp
shutil.copy(bs_, fp_)
tmppath = fpd_.replace(mpt, '')
return fp_, tmppath


def _mount(path, ftype, root=None):
mpt = None
if ftype == 'block':
mpt = tempfile.mkdtemp()
if not __salt__['mount.mount'](mpt, path):
os.rmdir(mpt)
return None
elif ftype == 'dir':
return path
elif ftype == 'file':
if 'guestfs.mount' in __salt__:
util = 'guestfs'
elif 'qemu_nbd.init' in __salt__:
util = 'qemu_nbd'
else:
return None
mpt = __salt__['mount.mount'](path, device=root, util=util)
if not mpt:
return None
return mpt


def _umount(mpt, ftype):
if ftype == 'block':
__salt__['mount.umount'](mpt)
os.rmdir(mpt)
elif ftype == 'file':
__salt__['mount.umount'](mpt, util='qemu_nbd')


def apply_(path, id_=None, config=None, approve_key=True, install=True,
prep_install=False, pub_key=None, priv_key=None, mount_point=None):
'''
Seed a location (disk image, directory, or block device) with the
minion config, approve the minion's key, and/or install salt-minion.

CLI Example:

.. code-block:: bash

salt 'minion' seed.apply path id [config=config_data] \\
[gen_key=(true|false)] [approve_key=(true|false)] \\
[install=(true|false)]

path
Full path to the directory, device, or disk image on the target
minion's file system.

id
Minion id with which to seed the path.

config
Minion configuration options. By default, the 'master' option is set to
the target host's 'master'.

approve_key
Request a pre-approval of the generated minion key. Requires
that the salt-master be configured to either auto-accept all keys or
expect a signing request from the target host. Default: true.

install
Install salt-minion, if absent. Default: true.

prep_install
Prepare the bootstrap script, but don't run it. Default: false
'''
stats = __salt__['file.stats'](path, follow_symlinks=True)
if not stats:
return '{0} does not exist'.format(path)
ftype = stats['type']
path = stats['target']
log.debug('Mounting {0} at {1}'.format(ftype, path))
try:
os.makedirs(path)
except OSError:
# The directory already exists
pass

mpt = _mount(path, ftype, mount_point)

if not mpt:
return '{0} could not be mounted'.format(path)

tmp = os.path.join(mpt, 'tmp')
log.debug('Attempting to create directory {0}'.format(tmp))
try:
os.makedirs(tmp)
except OSError:
if not os.path.isdir(tmp):
raise
cfg_files = mkconfig(config, tmp=tmp, id_=id_, approve_key=approve_key,
pub_key=pub_key, priv_key=priv_key)

if _check_install(mpt):
# salt-minion is already installed, just move the config and keys
# into place
log.info('salt-minion pre-installed on image, '
'configuring as {0}'.format(id_))
minion_config = salt.config.minion_config(cfg_files['config'])
pki_dir = minion_config['pki_dir']
if not os.path.isdir(os.path.join(mpt, pki_dir.lstrip('/'))):
__salt__['file.makedirs'](
os.path.join(mpt, pki_dir.lstrip('/'), '')
)
os.rename(cfg_files['privkey'], os.path.join(
mpt, pki_dir.lstrip('/'), 'minion.pem'))
os.rename(cfg_files['pubkey'], os.path.join(
mpt, pki_dir.lstrip('/'), 'minion.pub'))
os.rename(cfg_files['config'], os.path.join(mpt, 'etc/salt/minion'))
res = True
elif install:
log.info('Attempting to install salt-minion to {0}'.format(mpt))
res = _install(mpt)
elif prep_install:
log.error('The prep_install option is no longer supported. Please use '
'the bootstrap script installed with Salt, located at {0}.'
.format(salt.syspaths.BOOTSTRAP))
res = False
else:
log.warning('No useful action performed on {0}'.format(mpt))
res = False

_umount(mpt, ftype)
return res


def mkconfig(config=None,
tmp=None,
id_=None,
approve_key=True,
pub_key=None,
priv_key=None):
'''
Generate keys and config and put them in a tmp directory.

pub_key
absolute path or file content of an optional preseeded salt key

priv_key
absolute path or file content of an optional preseeded salt key

CLI Example:

.. code-block:: bash

salt 'minion' seed.mkconfig [config=config_data] [tmp=tmp_dir] \\
[id_=minion_id] [approve_key=(true|false)]
'''
if tmp is None:
tmp = tempfile.mkdtemp()
if config is None:
config = {}
if 'master' not in config and __opts__['master'] != 'salt':
config['master'] = __opts__['master']
if id_:
config['id'] = id_

# Write the new minion's config to a tmp file
tmp_config = os.path.join(tmp, 'minion')
with salt.utils.fopen(tmp_config, 'w+') as fp_:
fp_.write(salt.utils.cloud.salt_config_to_yaml(config))

# Generate keys for the minion
pubkeyfn = os.path.join(tmp, 'minion.pub')
privkeyfn = os.path.join(tmp, 'minion.pem')
preseeded = pub_key and priv_key
if preseeded:
log.debug('Writing minion.pub to {0}'.format(pubkeyfn))
log.debug('Writing minion.pem to {0}'.format(privkeyfn))
with salt.utils.fopen(pubkeyfn, 'w') as fic:
fic.write(_file_or_content(pub_key))
with salt.utils.fopen(privkeyfn, 'w') as fic:
fic.write(_file_or_content(priv_key))
os.chmod(pubkeyfn, 0o600)
os.chmod(privkeyfn, 0o600)
else:
salt.crypt.gen_keys(tmp, 'minion', 2048)
if approve_key and not preseeded:
with salt.utils.fopen(pubkeyfn) as fp_:
pubkey = fp_.read()
__salt__['pillar.ext']({'virtkey': [id_, pubkey]})

return {'config': tmp_config, 'pubkey': pubkeyfn, 'privkey': privkeyfn}


def _install(mpt):
'''
Determine whether salt-minion is installed and, if not,
install it.
Return True if install is successful or already installed.
'''
_check_resolv(mpt)
boot_, tmppath = (prep_bootstrap(mpt)
or salt.syspaths.BOOTSTRAP)
# Exec the chroot command
cmd = 'if type salt-minion; then exit 0; '
cmd += 'else sh {0} -c /tmp; fi'.format(os.path.join(tmppath, 'bootstrap-salt.sh'))
return not __salt__['cmd.run_chroot'](mpt, cmd, python_shell=True)['retcode']


def _check_resolv(mpt):
'''
Check that the resolv.conf is present and populated
'''
resolv = os.path.join(mpt, 'etc/resolv.conf')
replace = False
if os.path.islink(resolv):
resolv = os.path.realpath(resolv)
if not os.path.isdir(os.path.dirname(resolv)):
os.makedirs(os.path.dirname(resolv))
if not os.path.isfile(resolv):
replace = True
if not replace:
with salt.utils.fopen(resolv, 'rb') as fp_:
conts = fp_.read()
if 'nameserver' not in conts:
replace = True
if replace:
shutil.copy('/etc/resolv.conf', resolv)


def _check_install(root):
sh_ = '/bin/sh'
if os.path.isfile(os.path.join(root, 'bin/bash')):
sh_ = '/bin/bash'

cmd = ('if ! type salt-minion; then exit 1; fi')
cmd = 'chroot \'{0}\' {1} -c \'{2}\''.format(
root,
sh_,
cmd)

return not __salt__['cmd.retcode'](cmd,
output_loglevel='quiet',
python_shell=True)

+ 1
- 1
_modules/virtng.py View File

@@ -622,7 +622,7 @@ def init(name,

if kwargs.get('seed') and seedable:
install = kwargs.get('install', True)
seed_cmd = kwargs.get('seed_cmd', 'seed.apply')
seed_cmd = kwargs.get('seed_cmd', 'seedng.apply')

__salt__[seed_cmd](img_dest,
id_=name,

Loading…
Cancel
Save