Merge branch 'integration'
Conflicts: lib/ansible/playbook.py lib/ansible/runner.py library/apt
This commit is contained in:
commit
f4bca7ea22
7 changed files with 404 additions and 161 deletions
65
apt
65
apt
|
@ -42,7 +42,7 @@ def fail_json(**kwargs):
|
||||||
exit_json(rc=1, **kwargs)
|
exit_json(rc=1, **kwargs)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import apt
|
import apt, apt_pkg
|
||||||
except ImportError:
|
except ImportError:
|
||||||
fail_json(msg="could not import apt, please install the python-apt package on this host")
|
fail_json(msg="could not import apt, please install the python-apt package on this host")
|
||||||
|
|
||||||
|
@ -63,17 +63,30 @@ def run_apt(command):
|
||||||
rc = cmd.returncode
|
rc = cmd.returncode
|
||||||
return rc, out, err
|
return rc, out, err
|
||||||
|
|
||||||
def package_status(pkgspec, cache):
|
def package_split(pkgspec):
|
||||||
try:
|
parts = pkgspec.split('=')
|
||||||
pkg = cache[pkgspec]
|
if len(parts) > 1:
|
||||||
except:
|
return parts[0], parts[1]
|
||||||
fail_json(msg="No package matching '%s' is available" % pkgspec)
|
else:
|
||||||
return (pkg.is_installed, pkg.is_upgradable)
|
return parts[0], None
|
||||||
|
|
||||||
def install(pkgspec, cache, upgrade=False):
|
def package_status(pkgname, version, cache):
|
||||||
(installed, upgradable) = package_status(pkgspec, cache)
|
try:
|
||||||
if (not installed) or (upgrade and upgradable):
|
pkg = cache[pkgname]
|
||||||
|
except KeyError:
|
||||||
|
fail_json(msg="No package matching '%s' is available" % pkgname)
|
||||||
|
if version:
|
||||||
|
return pkg.is_installed and pkg.installed.version == version, False
|
||||||
|
else:
|
||||||
|
return pkg.is_installed, pkg.is_upgradable
|
||||||
|
|
||||||
|
def install(pkgspec, cache, upgrade=False, default_release=None):
|
||||||
|
name, version = package_split(pkgspec)
|
||||||
|
installed, upgradable = package_status(name, version, cache)
|
||||||
|
if not installed or (upgrade and upgradable):
|
||||||
cmd = "%s -q -y install '%s'" % (APT, pkgspec)
|
cmd = "%s -q -y install '%s'" % (APT, pkgspec)
|
||||||
|
if default_release:
|
||||||
|
cmd += " -t '%s'" % (default_release,)
|
||||||
rc, out, err = run_apt(cmd)
|
rc, out, err = run_apt(cmd)
|
||||||
if rc:
|
if rc:
|
||||||
fail_json(msg="'apt-get install %s' failed: %s" % (pkgspec, err))
|
fail_json(msg="'apt-get install %s' failed: %s" % (pkgspec, err))
|
||||||
|
@ -82,15 +95,16 @@ def install(pkgspec, cache, upgrade=False):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def remove(pkgspec, cache, purge=False):
|
def remove(pkgspec, cache, purge=False):
|
||||||
(installed, upgradable) = package_status(pkgspec, cache)
|
name, version = package_split(pkgspec)
|
||||||
|
installed, upgradable = package_status(name, version, cache)
|
||||||
if not installed:
|
if not installed:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
purge = '--purge' if purge else ''
|
purge = '--purge' if purge else ''
|
||||||
cmd = "%s -q -y %s remove '%s'" % (APT, purge, pkgspec)
|
cmd = "%s -q -y %s remove '%s'" % (APT, purge, name)
|
||||||
rc, out, err = run_apt(cmd)
|
rc, out, err = run_apt(cmd)
|
||||||
if rc:
|
if rc:
|
||||||
fail_json(msg="'apt-get remove %s' failed: %s" % (pkgspec, err))
|
fail_json(msg="'apt-get remove %s' failed: %s" % (name, err))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -109,13 +123,14 @@ if not len(items):
|
||||||
|
|
||||||
params = {}
|
params = {}
|
||||||
for x in items:
|
for x in items:
|
||||||
(k, v) = x.split("=")
|
(k, v) = x.split("=", 1)
|
||||||
params[k] = v
|
params[k] = v
|
||||||
|
|
||||||
state = params.get('state','installed')
|
state = params.get('state', 'installed')
|
||||||
package = params.get('pkg', params.get('package', params.get('name', None)))
|
package = params.get('pkg', params.get('package', params.get('name', None)))
|
||||||
update_cache = params.get('update-cache', 'no')
|
update_cache = params.get('update-cache', 'no')
|
||||||
purge = params.get('purge', 'no')
|
purge = params.get('purge', 'no')
|
||||||
|
default_release = params.get('default-release', None)
|
||||||
|
|
||||||
if state not in ['installed', 'latest', 'removed']:
|
if state not in ['installed', 'latest', 'removed']:
|
||||||
fail_json(msg='invalid state')
|
fail_json(msg='invalid state')
|
||||||
|
@ -130,6 +145,10 @@ if package is None and update_cache != 'yes':
|
||||||
fail_json(msg='pkg=name and/or update-cache=yes is required')
|
fail_json(msg='pkg=name and/or update-cache=yes is required')
|
||||||
|
|
||||||
cache = apt.Cache()
|
cache = apt.Cache()
|
||||||
|
if default_release:
|
||||||
|
apt_pkg.config['APT::Default-Release'] = default_release
|
||||||
|
# reopen cache w/ modified config
|
||||||
|
cache.open()
|
||||||
|
|
||||||
if update_cache == 'yes':
|
if update_cache == 'yes':
|
||||||
cache.update()
|
cache.update()
|
||||||
|
@ -137,10 +156,16 @@ if update_cache == 'yes':
|
||||||
if package == None:
|
if package == None:
|
||||||
exit_json(changed=False)
|
exit_json(changed=False)
|
||||||
|
|
||||||
|
if package.count('=') > 1:
|
||||||
|
fail_json(msg='invalid package spec')
|
||||||
|
|
||||||
if state == 'latest':
|
if state == 'latest':
|
||||||
changed = install(package, cache, upgrade=True)
|
if '=' in package:
|
||||||
|
fail_json(msg='version number inconsistent with state=latest')
|
||||||
|
changed = install(package, cache, upgrade=True,
|
||||||
|
default_release=default_release)
|
||||||
elif state == 'installed':
|
elif state == 'installed':
|
||||||
changed = install(package, cache)
|
changed = install(package, cache, default_release=default_release)
|
||||||
elif state == 'removed':
|
elif state == 'removed':
|
||||||
changed = remove(package, cache, purge == 'yes')
|
changed = remove(package, cache, purge == 'yes')
|
||||||
|
|
||||||
|
|
5
copy
5
copy
|
@ -42,7 +42,10 @@ for x in items:
|
||||||
|
|
||||||
src = params['src']
|
src = params['src']
|
||||||
dest = params['dest']
|
dest = params['dest']
|
||||||
|
if src:
|
||||||
|
src = os.path.expanduser(src)
|
||||||
|
if dest:
|
||||||
|
dest = os.path.expanduser(dest)
|
||||||
|
|
||||||
# raise an error if there is no src file
|
# raise an error if there is no src file
|
||||||
if not os.path.exists(src):
|
if not os.path.exists(src):
|
||||||
|
|
53
file
53
file
|
@ -72,6 +72,21 @@ def add_path_info(kwargs):
|
||||||
kwargs['state'] = 'absent'
|
kwargs['state'] = 'absent'
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
# If selinux fails to find a default, return an array of None
|
||||||
|
def selinux_default_context(path, mode=0):
|
||||||
|
context = [None, None, None, None]
|
||||||
|
if not HAVE_SELINUX:
|
||||||
|
return context
|
||||||
|
try:
|
||||||
|
ret = selinux.matchpathcon(path, mode)
|
||||||
|
except OSError:
|
||||||
|
return context
|
||||||
|
if ret[0] == -1:
|
||||||
|
return context
|
||||||
|
context = ret[1].split(':')
|
||||||
|
debug("got default secontext=%s" % ret[1])
|
||||||
|
return context
|
||||||
|
|
||||||
# ===========================================
|
# ===========================================
|
||||||
|
|
||||||
argfile = sys.argv[1]
|
argfile = sys.argv[1]
|
||||||
|
@ -89,7 +104,11 @@ for x in items:
|
||||||
|
|
||||||
state = params.get('state','file')
|
state = params.get('state','file')
|
||||||
path = params.get('path', params.get('dest', params.get('name', None)))
|
path = params.get('path', params.get('dest', params.get('name', None)))
|
||||||
|
if path:
|
||||||
|
path = os.path.expanduser(path)
|
||||||
src = params.get('src', None)
|
src = params.get('src', None)
|
||||||
|
if src:
|
||||||
|
src = os.path.expanduser(src)
|
||||||
dest = params.get('dest', None)
|
dest = params.get('dest', None)
|
||||||
mode = params.get('mode', None)
|
mode = params.get('mode', None)
|
||||||
owner = params.get('owner', None)
|
owner = params.get('owner', None)
|
||||||
|
@ -102,8 +121,16 @@ recurse = params.get('recurse', 'false')
|
||||||
seuser = params.get('seuser', None)
|
seuser = params.get('seuser', None)
|
||||||
serole = params.get('serole', None)
|
serole = params.get('serole', None)
|
||||||
setype = params.get('setype', None)
|
setype = params.get('setype', None)
|
||||||
serange = params.get('serange', 's0')
|
selevel = params.get('serange', 's0')
|
||||||
secontext = [seuser, serole, setype, serange]
|
context = params.get('context', None)
|
||||||
|
secontext = [seuser, serole, setype, selevel]
|
||||||
|
|
||||||
|
if context is not None:
|
||||||
|
if context != 'default':
|
||||||
|
fail_json(msg='invalid context: %s' % context)
|
||||||
|
if seuser is not None or serole is not None or setype is not None:
|
||||||
|
fail_json(msg='cannot define context=default and seuser, serole or setype')
|
||||||
|
secontext = selinux_default_context(path)
|
||||||
|
|
||||||
if state not in [ 'file', 'directory', 'link', 'absent']:
|
if state not in [ 'file', 'directory', 'link', 'absent']:
|
||||||
fail_json(msg='invalid state: %s' % state)
|
fail_json(msg='invalid state: %s' % state)
|
||||||
|
@ -144,34 +171,14 @@ def selinux_context(path):
|
||||||
debug("got current secontext=%s" % ret[1])
|
debug("got current secontext=%s" % ret[1])
|
||||||
return context
|
return context
|
||||||
|
|
||||||
# If selinux fails to find a default, return an array of None
|
|
||||||
def selinux_default_context(path, mode=0):
|
|
||||||
context = [None, None, None, None]
|
|
||||||
print >>sys.stderr, path
|
|
||||||
if not HAVE_SELINUX:
|
|
||||||
return context
|
|
||||||
try:
|
|
||||||
ret = selinux.matchpathcon(path, mode)
|
|
||||||
except OSError:
|
|
||||||
return context
|
|
||||||
if ret[0] == -1:
|
|
||||||
return context
|
|
||||||
context = ret[1].split(':')
|
|
||||||
debug("got default secontext=%s" % ret[1])
|
|
||||||
return context
|
|
||||||
|
|
||||||
def set_context_if_different(path, context, changed):
|
def set_context_if_different(path, context, changed):
|
||||||
if not HAVE_SELINUX:
|
if not HAVE_SELINUX:
|
||||||
return changed
|
return changed
|
||||||
cur_context = selinux_context(path)
|
cur_context = selinux_context(path)
|
||||||
new_context = selinux_default_context(path)
|
new_context = list(cur_context)
|
||||||
for i in range(len(context)):
|
for i in range(len(context)):
|
||||||
if context[i] is not None and context[i] != cur_context[i]:
|
if context[i] is not None and context[i] != cur_context[i]:
|
||||||
debug('new context was %s' % new_context[i])
|
|
||||||
new_context[i] = context[i]
|
new_context[i] = context[i]
|
||||||
debug('new context is %s' % new_context[i])
|
|
||||||
elif new_context[i] is None:
|
|
||||||
new_context[i] = cur_context[i]
|
|
||||||
debug("current secontext is %s" % ':'.join(cur_context))
|
debug("current secontext is %s" % ':'.join(cur_context))
|
||||||
debug("new secontext is %s" % ':'.join(new_context))
|
debug("new secontext is %s" % ':'.join(new_context))
|
||||||
if cur_context != new_context:
|
if cur_context != new_context:
|
||||||
|
|
249
setup
249
setup
|
@ -19,9 +19,16 @@
|
||||||
|
|
||||||
DEFAULT_ANSIBLE_SETUP = "/etc/ansible/setup"
|
DEFAULT_ANSIBLE_SETUP = "/etc/ansible/setup"
|
||||||
|
|
||||||
|
import array
|
||||||
|
import fcntl
|
||||||
|
import glob
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
import subprocess
|
import subprocess
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
@ -30,6 +37,244 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
|
|
||||||
|
_I386RE = re.compile(r'i[3456]86')
|
||||||
|
SIOCGIFCONF = 0x8912
|
||||||
|
SIOCGIFHWADDR = 0x8927
|
||||||
|
MEMORY_FACTS = ['MemTotal', 'SwapTotal', 'MemFree', 'SwapFree']
|
||||||
|
# DMI bits
|
||||||
|
DMI_DICT = { 'form_factor': '/sys/devices/virtual/dmi/id/chassis_type',
|
||||||
|
'product_name': '/sys/devices/virtual/dmi/id/product_name',
|
||||||
|
'product_serial': '/sys/devices/virtual/dmi/id/product_serial',
|
||||||
|
'product_uuid': '/sys/devices/virtual/dmi/id/product_uuid',
|
||||||
|
'product_version': '/sys/devices/virtual/dmi/id/product_version',
|
||||||
|
'system_vendor': '/sys/devices/virtual/dmi/id/sys_vendor' }
|
||||||
|
# From smolt and DMI spec
|
||||||
|
FORM_FACTOR = [ "Unknown", "Other", "Unknown", "Desktop",
|
||||||
|
"Low Profile Desktop", "Pizza Box", "Mini Tower", "Tower",
|
||||||
|
"Portable", "Laptop", "Notebook", "Hand Held", "Docking Station",
|
||||||
|
"All In One", "Sub Notebook", "Space-saving", "Lunch Box",
|
||||||
|
"Main Server Chassis", "Expansion Chassis", "Sub Chassis",
|
||||||
|
"Bus Expansion Chassis", "Peripheral Chassis", "RAID Chassis",
|
||||||
|
"Rack Mount Chassis", "Sealed-case PC", "Multi-system",
|
||||||
|
"CompactPCI", "AdvancedTCA" ]
|
||||||
|
# For the most part, we assume that platform.dist() will tell the truth.
|
||||||
|
# This is the fallback to handle unknowns or exceptions
|
||||||
|
OSDIST_DICT = { '/etc/redhat-release': 'RedHat',
|
||||||
|
'/etc/vmware-release': 'VMwareESX' }
|
||||||
|
|
||||||
|
def get_file_content(path):
|
||||||
|
if os.path.exists(path) and os.access(path, os.R_OK):
|
||||||
|
data = open(path).read().strip()
|
||||||
|
if len(data) == 0:
|
||||||
|
data = None
|
||||||
|
else:
|
||||||
|
data = None
|
||||||
|
return data
|
||||||
|
|
||||||
|
# platform.dist() is deprecated in 2.6
|
||||||
|
# in 2.6 and newer, you should use platform.linux_distribution()
|
||||||
|
def get_distribution_facts(facts):
|
||||||
|
dist = platform.dist()
|
||||||
|
facts['distribution'] = dist[0].capitalize() or 'NA'
|
||||||
|
facts['distribution_version'] = dist[1] or 'NA'
|
||||||
|
facts['distribution_release'] = dist[2] or 'NA'
|
||||||
|
# Try to handle the exceptions now ...
|
||||||
|
for (path, name) in OSDIST_DICT.items():
|
||||||
|
if os.path.exists(path):
|
||||||
|
if facts['distribution'] == 'Fedora':
|
||||||
|
pass
|
||||||
|
elif name == 'RedHat':
|
||||||
|
data = get_file_content(path)
|
||||||
|
if 'Red Hat' in data:
|
||||||
|
facts['distribution'] = name
|
||||||
|
else:
|
||||||
|
facts['distribution'] = data.split()[0]
|
||||||
|
else:
|
||||||
|
facts['distribution'] = name
|
||||||
|
|
||||||
|
# Platform
|
||||||
|
# patform.system() can be Linux, Darwin, Java, or Windows
|
||||||
|
def get_platform_facts(facts):
|
||||||
|
facts['system'] = platform.system()
|
||||||
|
facts['kernel'] = platform.release()
|
||||||
|
facts['machine'] = platform.machine()
|
||||||
|
facts['python_version'] = platform.python_version()
|
||||||
|
if facts['machine'] == 'x86_64':
|
||||||
|
facts['architecture'] = facts['machine']
|
||||||
|
elif _I386RE.search(facts['machine']):
|
||||||
|
facts['architecture'] = 'i386'
|
||||||
|
else:
|
||||||
|
facts['archtecture'] = facts['machine']
|
||||||
|
if facts['system'] == 'Linux':
|
||||||
|
get_distribution_facts(facts)
|
||||||
|
|
||||||
|
def get_memory_facts(facts):
|
||||||
|
if not os.access("/proc/meminfo", os.R_OK):
|
||||||
|
return facts
|
||||||
|
for line in open("/proc/meminfo").readlines():
|
||||||
|
data = line.split(":", 1)
|
||||||
|
key = data[0]
|
||||||
|
if key in MEMORY_FACTS:
|
||||||
|
val = data[1].strip().split(' ')[0]
|
||||||
|
facts["%s_mb" % key.lower()] = long(val) / 1024
|
||||||
|
|
||||||
|
def get_cpu_facts(facts):
|
||||||
|
i = 0
|
||||||
|
physid = 0
|
||||||
|
sockets = {}
|
||||||
|
if not os.access("/proc/cpuinfo", os.R_OK):
|
||||||
|
return facts
|
||||||
|
for line in open("/proc/cpuinfo").readlines():
|
||||||
|
data = line.split(":", 1)
|
||||||
|
key = data[0].strip()
|
||||||
|
if key == 'model name':
|
||||||
|
if 'processor' not in facts:
|
||||||
|
facts['processor'] = []
|
||||||
|
facts['processor'].append(data[1].strip())
|
||||||
|
i += 1
|
||||||
|
elif key == 'physical id':
|
||||||
|
physid = data[1].strip()
|
||||||
|
if physid not in sockets:
|
||||||
|
sockets[physid] = 1
|
||||||
|
elif key == 'cpu cores':
|
||||||
|
sockets[physid] = int(data[1].strip())
|
||||||
|
if len(sockets) > 0:
|
||||||
|
facts['processor_count'] = len(sockets)
|
||||||
|
facts['processor_cores'] = reduce(lambda x, y: x + y, sockets.values())
|
||||||
|
else:
|
||||||
|
facts['processor_count'] = i
|
||||||
|
facts['processor_cores'] = 'NA'
|
||||||
|
|
||||||
|
def get_hardware_facts(facts):
|
||||||
|
get_memory_facts(facts)
|
||||||
|
get_cpu_facts(facts)
|
||||||
|
for (key,path) in DMI_DICT.items():
|
||||||
|
data = get_file_content(path)
|
||||||
|
if data is not None:
|
||||||
|
if key == 'form_factor':
|
||||||
|
facts['form_factor'] = FORM_FACTOR[int(data)]
|
||||||
|
else:
|
||||||
|
facts[key] = data
|
||||||
|
else:
|
||||||
|
facts[key] = 'NA'
|
||||||
|
|
||||||
|
def get_linux_virtual_facts(facts):
|
||||||
|
if os.path.exists("/proc/xen"):
|
||||||
|
facts['virtualization_type'] = 'xen'
|
||||||
|
facts['virtualization_role'] = 'guest'
|
||||||
|
if os.path.exists("/proc/xen/capabilities"):
|
||||||
|
facts['virtualization_role'] = 'host'
|
||||||
|
if os.path.exists("/proc/modules"):
|
||||||
|
modules = []
|
||||||
|
for line in open("/proc/modules").readlines():
|
||||||
|
data = line.split(" ", 1)
|
||||||
|
modules.append(data[0])
|
||||||
|
if 'kvm' in modules:
|
||||||
|
facts['virtualization_type'] = 'kvm'
|
||||||
|
facts['virtualization_role'] = 'host'
|
||||||
|
elif 'vboxdrv' in modules:
|
||||||
|
facts['virtualization_type'] = 'virtualbox'
|
||||||
|
facts['virtualization_role'] = 'host'
|
||||||
|
elif 'vboxguest' in modules:
|
||||||
|
facts['virtualization_type'] = 'virtualbox'
|
||||||
|
facts['virtualization_role'] = 'guest'
|
||||||
|
if 'QEMU' in facts['processor'][0]:
|
||||||
|
facts['virtualization_type'] = 'kvm'
|
||||||
|
facts['virtualization_role'] = 'guest'
|
||||||
|
if facts['distribution'] == 'VMwareESX':
|
||||||
|
facts['virtualization_type'] = 'VMware'
|
||||||
|
facts['virtualization_role'] = 'host'
|
||||||
|
# You can spawn a dmidecode process and parse that or infer from devices
|
||||||
|
for dev_model in glob.glob('/proc/ide/hd*/model'):
|
||||||
|
info = open(dev_model).read()
|
||||||
|
if 'VMware' in info:
|
||||||
|
facts['virtualization_type'] = 'VMware'
|
||||||
|
facts['virtualization_role'] = 'guest'
|
||||||
|
elif 'Virtual HD' in info or 'Virtual CD' in info:
|
||||||
|
facts['virtualization_type'] = 'VirtualPC'
|
||||||
|
facts['virtualization_role'] = 'guest'
|
||||||
|
|
||||||
|
def get_virtual_facts(facts):
|
||||||
|
facts['virtualization_type'] = 'None'
|
||||||
|
facts['virtualization_role'] = 'None'
|
||||||
|
if facts['system'] == 'Linux':
|
||||||
|
facts = get_linux_virtual_facts(facts)
|
||||||
|
|
||||||
|
# get list of interfaces that are up
|
||||||
|
def get_interfaces():
|
||||||
|
length = 4096
|
||||||
|
offset = 32
|
||||||
|
step = 32
|
||||||
|
if platform.architecture()[0] == '64bit':
|
||||||
|
offset = 16
|
||||||
|
step = 40
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
names = array.array('B', '\0' * length)
|
||||||
|
bytelen = struct.unpack('iL', fcntl.ioctl(
|
||||||
|
s.fileno(), SIOCGIFCONF, struct.pack(
|
||||||
|
'iL', length, names.buffer_info()[0])
|
||||||
|
))[0]
|
||||||
|
return [names.tostring()[i:i+offset].split('\0', 1)[0]
|
||||||
|
for i in range(0, bytelen, step)]
|
||||||
|
|
||||||
|
def get_iface_hwaddr(iface):
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
info = fcntl.ioctl(s.fileno(), SIOCGIFHWADDR,
|
||||||
|
struct.pack('256s', iface[:15]))
|
||||||
|
return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1]
|
||||||
|
|
||||||
|
def get_network_facts(facts):
|
||||||
|
facts['fqdn'] = socket.gethostname()
|
||||||
|
facts['hostname'] = facts['fqdn'].split('.')[0]
|
||||||
|
facts['interfaces'] = get_interfaces()
|
||||||
|
for iface in facts['interfaces']:
|
||||||
|
facts[iface] = { 'macaddress': get_iface_hwaddr(iface) }
|
||||||
|
# This is lame, but there doesn't appear to be a good way
|
||||||
|
# to get all addresses for both IPv4 and IPv6.
|
||||||
|
cmd = subprocess.Popen("/sbin/ifconfig %s" % iface, shell=True,
|
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
out, err = cmd.communicate()
|
||||||
|
for line in out.split('\n'):
|
||||||
|
data = line.split()
|
||||||
|
if 'inet addr' in line:
|
||||||
|
if 'ipv4' not in facts[iface]:
|
||||||
|
facts[iface]['ipv4'] = {}
|
||||||
|
facts[iface]['ipv4'] = { 'address': data[1].split(':')[1],
|
||||||
|
'netmask': data[-1].split(':')[1] }
|
||||||
|
if 'inet6 addr' in line:
|
||||||
|
(ip, prefix) = data[2].split('/')
|
||||||
|
scope = data[3].split(':')[1].lower()
|
||||||
|
if 'ipv6' not in facts[iface]:
|
||||||
|
facts[iface]['ipv6'] = []
|
||||||
|
facts[iface]['ipv6'].append( { 'address': ip,
|
||||||
|
'prefix': prefix,
|
||||||
|
'scope': scope } )
|
||||||
|
return facts
|
||||||
|
|
||||||
|
def get_public_ssh_host_keys(facts):
|
||||||
|
dsa = get_file_content('/etc/ssh/ssh_host_dsa_key.pub')
|
||||||
|
rsa = get_file_content('/etc/ssh/ssh_host_rsa_key.pub')
|
||||||
|
if dsa is None:
|
||||||
|
dsa = 'NA'
|
||||||
|
else:
|
||||||
|
facts['ssh_host_key_dsa_public'] = dsa.split()[1]
|
||||||
|
if rsa is None:
|
||||||
|
rsa = 'NA'
|
||||||
|
else:
|
||||||
|
facts['ssh_host_key_rsa_public'] = rsa.split()[1]
|
||||||
|
|
||||||
|
def get_service_facts(facts):
|
||||||
|
get_public_ssh_host_keys(facts)
|
||||||
|
|
||||||
|
def ansible_facts():
|
||||||
|
facts = {}
|
||||||
|
get_platform_facts(facts)
|
||||||
|
get_hardware_facts(facts)
|
||||||
|
get_virtual_facts(facts)
|
||||||
|
get_network_facts(facts)
|
||||||
|
get_service_facts(facts)
|
||||||
|
return facts
|
||||||
|
|
||||||
# load config & template variables
|
# load config & template variables
|
||||||
|
|
||||||
if len(sys.argv) == 1:
|
if len(sys.argv) == 1:
|
||||||
|
@ -65,6 +310,10 @@ if not os.path.exists(ansible_file):
|
||||||
else:
|
else:
|
||||||
md5sum = os.popen("md5sum %s" % ansible_file).read().split()[0]
|
md5sum = os.popen("md5sum %s" % ansible_file).read().split()[0]
|
||||||
|
|
||||||
|
# Get some basic facts in case facter or ohai are not installed
|
||||||
|
for (k, v) in ansible_facts().items():
|
||||||
|
setup_options["ansible_%s" % k] = v
|
||||||
|
|
||||||
# if facter is installed, and we can use --json because
|
# if facter is installed, and we can use --json because
|
||||||
# ruby-json is ALSO installed, include facter data in the JSON
|
# ruby-json is ALSO installed, include facter data in the JSON
|
||||||
|
|
||||||
|
|
71
slurp
Executable file
71
slurp
Executable file
|
@ -0,0 +1,71 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
|
||||||
|
#
|
||||||
|
# This file is part of Ansible
|
||||||
|
#
|
||||||
|
# Ansible is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# Ansible is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import shlex
|
||||||
|
import base64
|
||||||
|
|
||||||
|
try:
|
||||||
|
import json
|
||||||
|
except ImportError:
|
||||||
|
import simplejson as json
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# convert arguments of form a=b c=d
|
||||||
|
# to a dictionary
|
||||||
|
|
||||||
|
if len(sys.argv) == 1:
|
||||||
|
sys.exit(1)
|
||||||
|
argfile = sys.argv[1]
|
||||||
|
if not os.path.exists(argfile):
|
||||||
|
sys.exit(1)
|
||||||
|
items = shlex.split(open(argfile, 'r').read())
|
||||||
|
|
||||||
|
params = {}
|
||||||
|
for x in items:
|
||||||
|
(k, v) = x.split("=")
|
||||||
|
params[k] = v
|
||||||
|
source = os.path.expanduser(params['src'])
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
|
||||||
|
# raise an error if there is no template metadata
|
||||||
|
if not os.path.exists(source):
|
||||||
|
print json.dumps(dict(
|
||||||
|
failed = 1,
|
||||||
|
msg = "file not found: %s" % source
|
||||||
|
))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if not os.access(source, os.R_OK):
|
||||||
|
print json.dumps(dict(
|
||||||
|
failed = 1,
|
||||||
|
msg = "file is not readable: %s" % source
|
||||||
|
))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
|
||||||
|
data = file(source).read()
|
||||||
|
data = base64.b64encode(data)
|
||||||
|
|
||||||
|
print json.dumps(dict(content=data, encoding='base64'))
|
||||||
|
sys.exit(0)
|
||||||
|
|
119
template
119
template
|
@ -17,119 +17,8 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import sys
|
# hey the Ansible template module isn't really a remote transferred
|
||||||
import os
|
# module. All the magic happens in Runner.py making use of the
|
||||||
import jinja2
|
# copy module, and if not running from a playbook, also the 'slurp'
|
||||||
import shlex
|
# module.
|
||||||
try:
|
|
||||||
import json
|
|
||||||
except ImportError:
|
|
||||||
import simplejson as json
|
|
||||||
|
|
||||||
environment = jinja2.Environment()
|
|
||||||
|
|
||||||
# ===========================================
|
|
||||||
# convert arguments of form a=b c=d
|
|
||||||
# to a dictionary
|
|
||||||
# FIXME: make more idiomatic
|
|
||||||
|
|
||||||
if len(sys.argv) == 1:
|
|
||||||
sys.exit(1)
|
|
||||||
argfile = sys.argv[1]
|
|
||||||
if not os.path.exists(argfile):
|
|
||||||
sys.exit(1)
|
|
||||||
items = shlex.split(open(argfile, 'r').read())
|
|
||||||
|
|
||||||
params = {}
|
|
||||||
for x in items:
|
|
||||||
(k, v) = x.split("=")
|
|
||||||
params[k] = v
|
|
||||||
|
|
||||||
source = params['src']
|
|
||||||
dest = params['dest']
|
|
||||||
metadata = params.get('metadata', '/etc/ansible/setup')
|
|
||||||
module_vars = params.get('vars')
|
|
||||||
|
|
||||||
# raise an error if there is no template metadata
|
|
||||||
if not os.path.exists(metadata):
|
|
||||||
print json.dumps({
|
|
||||||
"failed" : 1,
|
|
||||||
"msg" : "Missing %s, did you run the setup module yet?" % metadata
|
|
||||||
})
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# raise an error if we can't parse the template metadata
|
|
||||||
#data = {}
|
|
||||||
try:
|
|
||||||
f = open(metadata)
|
|
||||||
data = json.loads(f.read())
|
|
||||||
f.close()
|
|
||||||
except:
|
|
||||||
print json.dumps({
|
|
||||||
"failed" : 1,
|
|
||||||
"msg" : "Failed to parse/load %s, rerun the setup module?" % metadata
|
|
||||||
})
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if module_vars:
|
|
||||||
try:
|
|
||||||
f = open(module_vars)
|
|
||||||
vars = json.loads(f.read())
|
|
||||||
data.update(vars)
|
|
||||||
f.close()
|
|
||||||
except:
|
|
||||||
print json.dumps({
|
|
||||||
"failed" : 1,
|
|
||||||
"msg" : "Failed to parse/load %s." % module_vars
|
|
||||||
})
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if not os.path.exists(source):
|
|
||||||
print json.dumps({
|
|
||||||
"failed" : 1,
|
|
||||||
"msg" : "Source template could not be read: %s" % source
|
|
||||||
})
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
source = file(source).read()
|
|
||||||
|
|
||||||
if os.path.isdir(dest):
|
|
||||||
print json.dumps({
|
|
||||||
"failed" : 1,
|
|
||||||
"msg" : "Destination is a directory"
|
|
||||||
})
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# record md5sum of original source file so we can report if it changed
|
|
||||||
changed = False
|
|
||||||
md5sum = None
|
|
||||||
if os.path.exists(dest):
|
|
||||||
md5sum = os.popen("md5sum %s" % dest).read().split()[0]
|
|
||||||
|
|
||||||
try:
|
|
||||||
# call Jinja2 here and save the new template file
|
|
||||||
template = environment.from_string(source)
|
|
||||||
data_out = template.render(data)
|
|
||||||
except jinja2.TemplateError, e:
|
|
||||||
print json.dumps({
|
|
||||||
"failed": True,
|
|
||||||
"msg" : e.message
|
|
||||||
})
|
|
||||||
sys.exit(1)
|
|
||||||
f = open(dest, "w+")
|
|
||||||
f.write(data_out)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
# record m5sum and return success and whether things have changed
|
|
||||||
md5sum2 = os.popen("md5sum %s" % dest).read().split()[0]
|
|
||||||
|
|
||||||
if md5sum != md5sum2:
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
# mission accomplished
|
|
||||||
print json.dumps({
|
|
||||||
"md5sum" : md5sum2,
|
|
||||||
"changed" : changed
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
3
virt
3
virt
|
@ -10,8 +10,7 @@ This software may be freely redistributed under the terms of the GNU
|
||||||
general public license.
|
general public license.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program; if not, write to the Free Software
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VIRT_FAILED = 1
|
VIRT_FAILED = 1
|
||||||
|
|
Loading…
Reference in a new issue