Port the file module over to the new common code infrastructure + cleanup some redundant imports since the module code already imports those things.

This commit is contained in:
Michael DeHaan 2012-07-30 21:50:32 -04:00
parent 8e60ad986a
commit ce5f3dd148
15 changed files with 174 additions and 246 deletions

View file

@ -65,7 +65,3 @@ def main():
# this is magic, see lib/ansible/module_common.py # this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>> #<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main() main()

View file

@ -1,34 +0,0 @@
#!/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
try:
import json
except ImportError:
import simplejson as json
print >>sys.stderr, "THIS IS A TEST FAILURE"
print json.dumps({
"failed" : True,
"msg" : "this module always fails"
})

View file

@ -17,34 +17,16 @@
# 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 os
import sys
import shlex
import subprocess
import shutil import shutil
import stat import stat
import grp import grp
import pwd import pwd
import syslog
try: try:
import selinux import selinux
HAVE_SELINUX=True HAVE_SELINUX=True
except ImportError: except ImportError:
HAVE_SELINUX=False HAVE_SELINUX=False
def dump_kv(vars):
return " ".join("%s='%s'" % (k,v) for (k,v) in vars.items())
def exit_kv(rc=0, **kwargs):
if 'path' in kwargs:
add_path_info(kwargs)
print dump_kv(kwargs)
sys.exit(rc)
def fail_kv(**kwargs):
kwargs['failed'] = True
exit_kv(rc=1, **kwargs)
def add_path_info(kwargs): def add_path_info(kwargs):
path = kwargs['path'] path = kwargs['path']
if os.path.exists(path): if os.path.exists(path):
@ -66,6 +48,14 @@ def add_path_info(kwargs):
kwargs['state'] = 'absent' kwargs['state'] = 'absent'
return kwargs return kwargs
def module_exit_json(**kwargs):
add_path_info(kwargs)
module.exit_json(**kwargs)
def module_fail_json(**kwargs):
add_path_info(kwargs)
module.fail_json(**kwargs)
# Detect whether using selinux that is MLS-aware. # Detect whether using selinux that is MLS-aware.
# While this means you can set the level/range with # While this means you can set the level/range with
# selinux.lsetfilecon(), it may or may not mean that you # selinux.lsetfilecon(), it may or may not mean that you
@ -115,7 +105,7 @@ def selinux_context(path):
try: try:
ret = selinux.lgetfilecon(path) ret = selinux.lgetfilecon(path)
except: except:
fail_kv(path=path, msg='failed to retrieve selinux context') module_fail_json(path=path, msg='failed to retrieve selinux context')
if ret[0] == -1: if ret[0] == -1:
return context return context
context = ret[1].split(':') context = ret[1].split(':')
@ -123,56 +113,10 @@ def selinux_context(path):
# =========================================== # ===========================================
argfile = sys.argv[1]
args = open(argfile, 'r').read()
items = shlex.split(args)
syslog.openlog('ansible-%s' % os.path.basename(__file__))
syslog.syslog(syslog.LOG_NOTICE, 'Invoked with %s' % args)
if not len(items):
fail_kv(msg='the module requires arguments -a')
sys.exit(1)
params = {}
for x in items:
(k, v) = x.split("=",1)
params[k] = v
state = params.get('state','file')
path = params.get('path', params.get('dest', params.get('name', None)))
if path:
path = os.path.expanduser(path)
src = params.get('src', None)
if src:
src = os.path.expanduser(src)
dest = params.get('dest', None)
mode = params.get('mode', None)
owner = params.get('owner', None)
group = params.get('group', None)
# selinux related options
seuser = params.get('seuser', None)
serole = params.get('serole', None)
setype = params.get('setype', None)
selevel = params.get('serange', 's0')
secontext = [seuser, serole, setype]
if selinux_mls_enabled():
secontext.append(selevel)
default_secontext = selinux_default_context(path)
for i in range(len(default_secontext)):
if i is not None and secontext[i] == '_default':
secontext[i] = default_secontext[i]
if state not in [ 'file', 'directory', 'link', 'absent']:
fail_kv(msg='invalid state: %s' % state)
if state == 'link' and (src is None or dest is None):
fail_kv(msg='src and dest are required for "link" state')
elif path is None:
fail_kv(msg='path is required')
changed = False
# =========================================== # ===========================================
# support functions # support functions
@ -205,9 +149,9 @@ def set_context_if_different(path, context, changed):
try: try:
rc = selinux.lsetfilecon(path, ':'.join(new_context)) rc = selinux.lsetfilecon(path, ':'.join(new_context))
except OSError: except OSError:
fail_kv(path=path, msg='invalid selinux context') module_fail_json(path=path, msg='invalid selinux context')
if rc != 0: if rc != 0:
fail_kv(path=path, msg='set selinux context failed') module_fail_json(path=path, msg='set selinux context failed')
changed = True changed = True
return changed return changed
@ -218,7 +162,7 @@ def set_owner_if_different(path, owner, changed):
if owner != user: if owner != user:
rc = os.system("/bin/chown -R %s %s 2>/dev/null" % (owner, path)) rc = os.system("/bin/chown -R %s %s 2>/dev/null" % (owner, path))
if rc != 0: if rc != 0:
fail_kv(path=path, msg='chown failed') module_fail_json(path=path, msg='chown failed')
return True return True
return changed return changed
@ -230,7 +174,7 @@ def set_group_if_different(path, group, changed):
if old_group != group: if old_group != group:
rc = os.system("/bin/chgrp -R %s %s" % (group, path)) rc = os.system("/bin/chgrp -R %s %s" % (group, path))
if rc != 0: if rc != 0:
fail_kv(path=path, msg='chgrp failed') module_fail_json(path=path, msg='chgrp failed')
return True return True
return changed return changed
@ -241,7 +185,7 @@ def set_mode_if_different(path, mode, changed):
# FIXME: support English modes # FIXME: support English modes
mode = int(mode, 8) mode = int(mode, 8)
except Exception, e: except Exception, e:
fail_kv(path=path, msg='mode needs to be something octalish', details=str(e)) module_fail_json(path=path, msg='mode needs to be something octalish', details=str(e))
st = os.stat(path) st = os.stat(path)
prev_mode = stat.S_IMODE(st[stat.ST_MODE]) prev_mode = stat.S_IMODE(st[stat.ST_MODE])
@ -252,7 +196,7 @@ def set_mode_if_different(path, mode, changed):
try: try:
os.chmod(path, mode) os.chmod(path, mode)
except Exception, e: except Exception, e:
fail_kv(path=path, msg='chmod failed', details=str(e)) module_fail_json(path=path, msg='chmod failed', details=str(e))
st = os.stat(path) st = os.stat(path)
new_mode = stat.S_IMODE(st[stat.ST_MODE]) new_mode = stat.S_IMODE(st[stat.ST_MODE])
@ -263,98 +207,150 @@ def set_mode_if_different(path, mode, changed):
def rmtree_error(func, path, exc_info): def rmtree_error(func, path, exc_info):
fail_kv(path=path, msg='failed to remove directory') module_fail_json(path=path, msg='failed to remove directory')
# =========================================== # ===========================================
# go... # go...
prev_state = 'absent' def main():
if os.path.lexists(path):
if os.path.islink(path):
prev_state = 'link'
elif os.path.isfile(path):
prev_state = 'file'
else:
prev_state = 'directory'
if prev_state != 'absent' and state == 'absent': global module
try: module = AnsibleModule(
if prev_state == 'directory': argument_spec = dict(
if os.path.islink(path): state = dict(choices=['file','directory','link','absent'], default='file'),
os.unlink(path) path = dict(aliases=['dest', 'name'], required=True),
else: src = dict(),
shutil.rmtree(path, ignore_errors=False, onerror=rmtree_error) mode = dict(),
owner = dict(),
group = dict(),
seuser = dict(),
serole = dict(),
selevel = dict(),
secontext = dict(),
)
)
params = module.params
state = params['state']
path = os.path.expanduser(params['path'])
src = params.get('src', None)
if src:
src = os.path.expanduser(src)
mode = params.get('mode', None)
owner = params.get('owner', None)
group = params.get('group', None)
# selinux related options
seuser = params.get('seuser', None)
serole = params.get('serole', None)
setype = params.get('setype', None)
selevel = params.get('serange', 's0')
secontext = [seuser, serole, setype]
if selinux_mls_enabled():
secontext.append(selevel)
default_secontext = selinux_default_context(path)
for i in range(len(default_secontext)):
if i is not None and secontext[i] == '_default':
secontext[i] = default_secontext[i]
if state == 'link' and (src is None or path is None):
module_fail_json(msg='src and dest are required for "link" state')
elif path is None:
module_fail_json(msg='path is required')
changed = False
prev_state = 'absent'
if os.path.lexists(path):
if os.path.islink(path):
prev_state = 'link'
elif os.path.isfile(path):
prev_state = 'file'
else: else:
os.unlink(path) prev_state = 'directory'
except Exception, e:
fail_kv(path=path, msg=str(e))
exit_kv(path=path, changed=True)
sys.exit(0)
if prev_state != 'absent' and prev_state != state: if prev_state != 'absent' and state == 'absent':
fail_kv(path=path, msg='refusing to convert between %s and %s' % (prev_state, state)) try:
if prev_state == 'directory':
if os.path.islink(path):
os.unlink(path)
else:
shutil.rmtree(path, ignore_errors=False, onerror=rmtree_error)
else:
os.unlink(path)
except Exception, e:
module_fail_json(path=path, msg=str(e))
module_exit_json(path=path, changed=True)
if prev_state == 'absent' and state == 'absent': if prev_state != 'absent' and prev_state != state:
exit_kv(path=path, changed=False) module_fail_json(path=path, msg='refusing to convert between %s and %s' % (prev_state, state))
if state == 'file': if prev_state == 'absent' and state == 'absent':
module_exit_json(path=path, changed=False)
if prev_state == 'absent': if state == 'file':
fail_kv(path=path, msg='file does not exist, use copy or template module to create')
# set modes owners and context as needed
changed = set_context_if_different(path, secontext, changed)
changed = set_owner_if_different(path, owner, changed)
changed = set_group_if_different(path, group, changed)
changed = set_mode_if_different(path, mode, changed)
exit_kv(path=path, changed=changed)
elif state == 'directory':
if prev_state == 'absent':
os.makedirs(path)
changed = True
# set modes owners and context as needed if prev_state == 'absent':
changed = set_context_if_different(path, secontext, changed) module_fail_json(path=path, msg='file does not exist, use copy or template module to create')
changed = set_owner_if_different(path, owner, changed)
changed = set_group_if_different(path, group, changed)
changed = set_mode_if_different(path, mode, changed)
exit_kv(path=path, changed=changed) # set modes owners and context as needed
changed = set_context_if_different(path, secontext, changed)
changed = set_owner_if_different(path, owner, changed)
changed = set_group_if_different(path, group, changed)
changed = set_mode_if_different(path, mode, changed)
elif state == 'link': module_exit_json(path=path, changed=changed)
elif state == 'directory':
if prev_state == 'absent':
os.makedirs(path)
changed = True
# set modes owners and context as needed
changed = set_context_if_different(path, secontext, changed)
changed = set_owner_if_different(path, owner, changed)
changed = set_group_if_different(path, group, changed)
changed = set_mode_if_different(path, mode, changed)
module_exit_json(path=path, changed=changed)
elif state == 'link':
if os.path.isabs(src): if os.path.isabs(src):
abs_src = src abs_src = src
else: else:
abs_src = os.path.join(os.path.dirname(dest), src) abs_src = os.path.join(os.path.dirname(dest), src)
if not os.path.exists(abs_src): if not os.path.exists(abs_src):
fail_kv(dest=dest, src=src, msg='src file does not exist') module_fail_json(dest=dest, src=src, msg='src file does not exist')
if prev_state == 'absent': if prev_state == 'absent':
os.symlink(src, dest) os.symlink(src, path)
changed = True changed = True
elif prev_state == 'link': elif prev_state == 'link':
old_src = os.readlink(dest) old_src = os.readlink(path)
if not os.path.isabs(old_src): if not os.path.isabs(old_src):
old_src = os.path.join(os.path.dirname(dest), old_src) old_src = os.path.join(os.path.dirname(path), old_src)
if old_src != src: if old_src != src:
os.unlink(dest) os.unlink(path)
os.symlink(src, dest) os.symlink(src, path)
else: else:
fail_kv(dest=dest, src=src, msg='unexpected position reached') module_fail_json(dest=path, src=src, msg='unexpected position reached')
# set modes owners and context as needed # set modes owners and context as needed
changed = set_context_if_different(dest, secontext, changed) changed = set_context_if_different(path, secontext, changed)
changed = set_owner_if_different(dest, owner, changed) changed = set_owner_if_different(path, owner, changed)
changed = set_group_if_different(dest, group, changed) changed = set_group_if_different(path, group, changed)
changed = set_mode_if_different(dest, mode, changed) changed = set_mode_if_different(path, mode, changed)
exit_kv(dest=dest, src=src, changed=changed) module.exit_json(dest=path, src=src, changed=changed)
module_fail_json(path=path, msg='unexpected position reached')
# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()
fail_kv(path=path, msg='unexpected position reached')
sys.exit(0)

View file

@ -19,9 +19,7 @@
# #
# see examples/playbooks/get_url.yml # see examples/playbooks/get_url.yml
import os
import shutil import shutil
import syslog
import datetime import datetime
import tempfile import tempfile
@ -188,5 +186,4 @@ def main():
# this is magic, see lib/ansible/module_common.py # this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>> #<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main() main()

View file

@ -22,9 +22,7 @@
# tag. Latest is not supported, you should not be doing # tag. Latest is not supported, you should not be doing
# that. Contribs welcome! -- MPD # that. Contribs welcome! -- MPD
import os
import re import re
import subprocess
def get_version(dest): def get_version(dest):
''' samples the version of the git repo ''' ''' samples the version of the git repo '''

View file

@ -17,9 +17,7 @@
# 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 os
import grp import grp
import subprocess
def get_bin_path(module, arg): def get_bin_path(module, arg):
if os.path.exists('/usr/sbin/%s' % arg): if os.path.exists('/usr/sbin/%s' % arg):

View file

@ -19,13 +19,16 @@
# 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/>.
#mount module - mount fs and define in fstab # mount module - mount fs and define in fstab
#usage: # usage:
# mount name=mountpoint, src=device_to_be_mounted fstype=fstype opts=mount_opts, dump=0 passno=0 state=[present|absent|mounted|unmounted] #
# absent == remove from fstab and unmounted # mount name=mountpoint, src=device_to_be_mounted fstype=fstype
# present == add to fstab, do not change mount state # opts=mount_opts, dump=0 passno=0 state=[present|absent|mounted|unmounted]
# mounted == add to fstab if not there and make sure it is mounted #
# unmounted == do not change fstab state, but unmount # absent == remove from fstab and unmounted
# present == add to fstab, do not change mount state
# mounted == add to fstab if not there and make sure it is mounted
# unmounted == do not change fstab state, but unmount
def write_fstab(lines, dest): def write_fstab(lines, dest):
@ -37,14 +40,15 @@ def write_fstab(lines, dest):
fs_w.close() fs_w.close()
def set_mount(**kwargs): def set_mount(**kwargs):
"set/change a mount point location in fstab" """ set/change a mount point location in fstab """
# kwargs: name, src, fstype, opts, dump, passno, state, fstab=/etc/fstab # kwargs: name, src, fstype, opts, dump, passno, state, fstab=/etc/fstab
args = { args = dict(
'opts': 'defaults', opts = 'defaults',
'dump': '0', dump = '0',
'passno': '0', passno = '0',
'fstab': '/etc/fstab' fstab = '/etc/fstab'
} )
args.update(kwargs) args.update(kwargs)
new_line = '%(src)s %(name)s %(fstype)s %(opts)s %(dump)s %(passno)s\n' new_line = '%(src)s %(name)s %(fstype)s %(opts)s %(dump)s %(passno)s\n'
@ -84,7 +88,6 @@ def set_mount(**kwargs):
else: else:
to_write.append(line) to_write.append(line)
if not exists: if not exists:
to_write.append(new_line % args) to_write.append(new_line % args)
changed = True changed = True
@ -96,14 +99,15 @@ def set_mount(**kwargs):
def unset_mount(**kwargs): def unset_mount(**kwargs):
"remove a mount point from fstab" """ remove a mount point from fstab """
# kwargs: name, src, fstype, opts, dump, passno, state, fstab=/etc/fstab # kwargs: name, src, fstype, opts, dump, passno, state, fstab=/etc/fstab
args = { args = dict(
'opts': 'default', opts = 'default',
'dump': '0', dump = '0',
'passno': '0', passno = '0',
'fstab': '/etc/fstab' fstab = '/etc/fstab'
} )
args.update(kwargs) args.update(kwargs)
to_write = [] to_write = []
@ -138,7 +142,8 @@ def unset_mount(**kwargs):
def mount(**kwargs): def mount(**kwargs):
"mount up a path or remount if needed" """ mount up a path or remount if needed """
name = kwargs['name'] name = kwargs['name']
if os.path.ismount(name): if os.path.ismount(name):
cmd = ['/bin/mount', '-o', 'remount', name] cmd = ['/bin/mount', '-o', 'remount', name]
@ -153,7 +158,8 @@ def mount(**kwargs):
return call.returncode, out+err return call.returncode, out+err
def umount(**kwargs): def umount(**kwargs):
"unmount a path" """ unmount a path """
name = kwargs['name'] name = kwargs['name']
cmd = ['/bin/umount', name] cmd = ['/bin/umount', name]
@ -165,6 +171,7 @@ def umount(**kwargs):
return call.returncode, out+err return call.returncode, out+err
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict( argument_spec = dict(
state = dict(required=True, choices=['present', 'absent', 'mounted', 'unmounted']), state = dict(required=True, choices=['present', 'absent', 'mounted', 'unmounted']),
@ -194,7 +201,6 @@ def main():
if module.params['fstab'] is not None: if module.params['fstab'] is not None:
args['fstab'] = module.params['fstab'] args['fstab'] = module.params['fstab']
# absent == remove from fstab and unmounted # absent == remove from fstab and unmounted
# unmounted == do not change fstab state, but unmount # unmounted == do not change fstab state, but unmount
# present == add to fstab, do not change mount state # present == add to fstab, do not change mount state
@ -218,7 +224,6 @@ def main():
module.exit_json(changed=changed, **args) module.exit_json(changed=changed, **args)
if state == 'unmounted': if state == 'unmounted':
if os.path.ismount(name): if os.path.ismount(name):
res,msg = umount(**args) res,msg = umount(**args)
@ -228,8 +233,6 @@ def main():
module.exit_json(changed=changed, **args) module.exit_json(changed=changed, **args)
if state in ['mounted', 'present']: if state in ['mounted', 'present']:
name, changed = set_mount(**args) name, changed = set_mount(**args)
if state == 'mounted': if state == 'mounted':
@ -258,5 +261,4 @@ def main():
# this is magic, see lib/ansible/module_common.py # this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>> #<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main() main()

View file

@ -18,21 +18,16 @@
# along with Ansible. If not, see <http://www.gnu.org/licenses/>. # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# #
import subprocess
def get_facter_data(): def get_facter_data():
p = subprocess.Popen(["/usr/bin/env", "ohai"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) p = subprocess.Popen(["/usr/bin/env", "ohai"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(out, err) = p.communicate() (out, err) = p.communicate()
rc = p.returncode rc = p.returncode
return rc, out, err return rc, out, err
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict() argument_spec = dict()
) )
rc, out, err = get_facter_data() rc, out, err = get_facter_data()
if rc != 0: if rc != 0:
module.fail_json(msg=err) module.fail_json(msg=err)

View file

@ -17,8 +17,6 @@
# 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 base64
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict() argument_spec = dict()

View file

@ -26,19 +26,16 @@ else:
# PostgreSQL module specific support methods. # PostgreSQL module specific support methods.
# #
def db_exists(cursor, db): def db_exists(cursor, db):
query = "SELECT * FROM pg_database WHERE datname=%(db)s" query = "SELECT * FROM pg_database WHERE datname=%(db)s"
cursor.execute(query, {'db': db}) cursor.execute(query, {'db': db})
return cursor.rowcount == 1 return cursor.rowcount == 1
def db_delete(cursor, db): def db_delete(cursor, db):
query = "DROP DATABASE %s" % db query = "DROP DATABASE %s" % db
cursor.execute(query) cursor.execute(query)
return True return True
def db_create(cursor, db): def db_create(cursor, db):
query = "CREATE DATABASE %s" % db query = "CREATE DATABASE %s" % db
cursor.execute(query) cursor.execute(query)
@ -48,7 +45,6 @@ def db_create(cursor, db):
# Module execution. # Module execution.
# #
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec=dict( argument_spec=dict(

View file

@ -20,14 +20,10 @@
import array import array
import fcntl import fcntl
import glob import glob
import sys
import os
import platform import platform
import re import re
import socket import socket
import struct import struct
import subprocess
import traceback
try: try:
import selinux import selinux

View file

@ -17,8 +17,6 @@
# 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 base64
def main(): def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict( argument_spec = dict(

View file

@ -17,10 +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 os
import pwd import pwd
import grp import grp
import subprocess
try: try:
import spwd import spwd
HAVE_SPWD=True HAVE_SPWD=True

View file

@ -17,11 +17,6 @@ VIRT_FAILED = 1
VIRT_SUCCESS = 0 VIRT_SUCCESS = 0
VIRT_UNAVAILABLE=2 VIRT_UNAVAILABLE=2
# other modules
import os
import sys
import subprocess
try: try:
import libvirt import libvirt
except ImportError: except ImportError:

View file

@ -18,7 +18,6 @@
# along with Ansible. If not, see <http://www.gnu.org/licenses/>. # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# #
import yum import yum
import datetime import datetime
import traceback import traceback