mirror of https://github.com/ansible/ansible.git
Add group module to manage groups and group membership
This relies on groupadd, groupmod, groupdel, and gpasswd utilities on the system. You can optionally modify the gid for the group. You can also add/remove a user to/from a group with the option member. Member state is defined with the option memberstate.reviewable/pr18780/r1
parent
e44bf9e19c
commit
65667c5c9e
@ -0,0 +1,225 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# (c) 2012, Stephen Fromm <sfromm@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/>.
|
||||||
|
|
||||||
|
try:
|
||||||
|
import json
|
||||||
|
except ImportError:
|
||||||
|
import simplejson as json
|
||||||
|
import os
|
||||||
|
import grp
|
||||||
|
import shlex
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
GROUPADD = "/usr/sbin/groupadd"
|
||||||
|
GROUPDEL = "/usr/sbin/groupdel"
|
||||||
|
GROUPMOD = "/usr/sbin/groupmod"
|
||||||
|
GPASSWD = "/usr/bin/gpasswd"
|
||||||
|
|
||||||
|
def debug(msg):
|
||||||
|
# ansible ignores stderr, so it's safe to use for debug
|
||||||
|
print >>sys.stderr, msg
|
||||||
|
#pass
|
||||||
|
|
||||||
|
def exit_json(rc=0, **kwargs):
|
||||||
|
if 'name' in kwargs:
|
||||||
|
debug("add group info to exit_json")
|
||||||
|
add_group_info(kwargs)
|
||||||
|
print json.dumps(kwargs)
|
||||||
|
sys.exit(rc)
|
||||||
|
|
||||||
|
def fail_json(**kwargs):
|
||||||
|
kwargs['failed'] = True
|
||||||
|
exit_json(rc=1, **kwargs)
|
||||||
|
|
||||||
|
def add_group_info(kwargs):
|
||||||
|
name = kwargs['name']
|
||||||
|
if group_exists(name):
|
||||||
|
kwargs['state'] = 'present'
|
||||||
|
info = group_info(name)
|
||||||
|
kwargs['gid'] = info[2]
|
||||||
|
kwargs['members'] = info[3]
|
||||||
|
else:
|
||||||
|
kwargs['state'] = 'absent'
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
def group_del(group):
|
||||||
|
cmd = [GROUPDEL, group]
|
||||||
|
debug("Arguments to groupdel: %s" % (" ".join(cmd)))
|
||||||
|
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
if rc == 0:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def group_add(group, **kwargs):
|
||||||
|
cmd = [GROUPADD]
|
||||||
|
for key in kwargs:
|
||||||
|
if key == 'gid' and kwargs[key] is not None:
|
||||||
|
cmd.append('-g')
|
||||||
|
cmd.append(kwargs[key])
|
||||||
|
cmd.append(group)
|
||||||
|
debug("Arguments to groupadd: %s" % (" ".join(cmd)))
|
||||||
|
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
if rc == 0:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def group_mod(group, **kwargs):
|
||||||
|
cmd = [GROUPMOD]
|
||||||
|
info = group_info(group)
|
||||||
|
for key in kwargs:
|
||||||
|
if key == 'gid':
|
||||||
|
if kwargs[key] is not None and info[2] != int(kwargs[key]):
|
||||||
|
cmd.append('-g')
|
||||||
|
cmd.append(kwargs[key])
|
||||||
|
if len(cmd) == 1:
|
||||||
|
return False
|
||||||
|
cmd.append(group)
|
||||||
|
debug("Arguments to groupmod: %s" % (" ".join(cmd)))
|
||||||
|
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
if rc == 0:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def group_has_member(group, member):
|
||||||
|
if not group_exists(group):
|
||||||
|
return False
|
||||||
|
info = group_info(group)
|
||||||
|
if member in info[3]:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def group_add_member(group, member):
|
||||||
|
cmd = [GPASSWD, '-a', member, group]
|
||||||
|
debug("Arguments to gpasswd: %s" % (" ".join(cmd)))
|
||||||
|
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
if rc == 0:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def group_del_member(group, member):
|
||||||
|
cmd = [GPASSWD, '-d', member, group]
|
||||||
|
debug("Arguments to gpasswd: %s" % (" ".join(cmd)))
|
||||||
|
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
if rc == 0:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def group_exists(group):
|
||||||
|
try:
|
||||||
|
if grp.getgrnam(group):
|
||||||
|
return True
|
||||||
|
except KeyError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def group_info(group):
|
||||||
|
if not group_exists(group):
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
info = list(grp.getgrnam(group))
|
||||||
|
except KeyError:
|
||||||
|
return False
|
||||||
|
return info
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
|
||||||
|
if not os.path.exists(GROUPADD):
|
||||||
|
if os.path.exists("/sbin/groupadd"):
|
||||||
|
GROUPADD = "/sbin/groupadd"
|
||||||
|
else:
|
||||||
|
fail_json(msg="Cannot find groupadd")
|
||||||
|
if not os.path.exists(GROUPDEL):
|
||||||
|
if os.path.exists("/sbin/groupdel"):
|
||||||
|
GROUPDEL = "/sbin/groupdel"
|
||||||
|
else:
|
||||||
|
fail_json(msg="Cannot find groupdel")
|
||||||
|
if not os.path.exists(GROUPMOD):
|
||||||
|
if os.path.exists("/sbin/groupmod"):
|
||||||
|
GROUPDEL = "/sbin/groupmod"
|
||||||
|
else:
|
||||||
|
fail_json(msg="Cannot find groupmod")
|
||||||
|
if not os.path.exists(GPASSWD):
|
||||||
|
if os.path.exists("/bin/gpasswd"):
|
||||||
|
GROUPDEL = "/bin/gpasswd"
|
||||||
|
else:
|
||||||
|
fail_json(msg="Cannot find gpasswd")
|
||||||
|
|
||||||
|
if len(sys.argv) == 2 and os.path.exists(sys.argv[1]):
|
||||||
|
argfile = sys.argv[1]
|
||||||
|
args = open(argfile, 'r').read()
|
||||||
|
else:
|
||||||
|
args = ' '.join(sys.argv[1:])
|
||||||
|
items = shlex.split(args)
|
||||||
|
|
||||||
|
if not len(items):
|
||||||
|
fail_json(msg='the module requires arguments -a')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
params = {}
|
||||||
|
for x in items:
|
||||||
|
(k, v) = x.split("=")
|
||||||
|
params[k] = v
|
||||||
|
|
||||||
|
state = params.get('state','present')
|
||||||
|
name = params.get('name', None)
|
||||||
|
gid = params.get('gid', None)
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# the following controls group membership
|
||||||
|
member = params.get('member', None)
|
||||||
|
memberstate = params.get('memberstate', 'present')
|
||||||
|
|
||||||
|
if state not in [ 'present', 'absent' ]:
|
||||||
|
fail_json(msg='invalid state')
|
||||||
|
if memberstate not in [ 'present', 'absent' ]:
|
||||||
|
fail_json(msg='invalid memberstate')
|
||||||
|
if name is None:
|
||||||
|
fail_json(msg='name is required')
|
||||||
|
|
||||||
|
changed = False
|
||||||
|
rc = 0
|
||||||
|
if state == 'absent':
|
||||||
|
if group_exists(name):
|
||||||
|
changed = group_del(name)
|
||||||
|
exit_json(name=name, changed=changed)
|
||||||
|
elif state == 'present':
|
||||||
|
if not group_exists(name):
|
||||||
|
changed = group_add(name, gid=gid)
|
||||||
|
else:
|
||||||
|
changed = group_mod(name, gid=gid)
|
||||||
|
|
||||||
|
if member is not None:
|
||||||
|
if memberstate == 'present':
|
||||||
|
if not group_has_member(name, member):
|
||||||
|
changed = group_add_member(name, member)
|
||||||
|
elif memberstate == 'absent':
|
||||||
|
if group_has_member(name, member):
|
||||||
|
changed = group_del_member(name, member)
|
||||||
|
else:
|
||||||
|
fail_json(name=name, msg='Unexpected position reached')
|
||||||
|
|
||||||
|
exit_json(name=name, changed=changed)
|
||||||
|
|
||||||
|
fail_json(name=name, msg='Unexpected position reached')
|
Loading…
Reference in New Issue