| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551 |
- #! /usr/bin/python2
- # Authors:
- # Thierry Bordaz <[email protected]>
- #
- # Copyright (C) 2015 Red Hat
- # see file 'COPYING' for use and warranty information
- #
- # This program 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.
- #
- # This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
- import sys
- import os
- import argparse
- import pdb
- import tempfile
- import time
- import pwd
- import grp
- import platform
- import socket
- import shutil
- from subprocess import Popen, PIPE, STDOUT
- import string
- SETUP_DS = "/sbin/setup-ds.pl"
- REMOVE_DS = "/sbin/remove-ds.pl"
- INITCONFIGDIR = ".dirsrv"
- SCRIPT_START = "start-slapd"
- SCRIPT_STOP = "stop-slapd"
- SCRIPT_RESTART = "restart-slapd"
- ENVIRON_SERVERID = '389-SERVER-ID'
- ENVIRON_USER = '389-USER'
- ENVIRON_GROUP = '389-GROUP'
- ENVIRON_DIRECTORY = '389-DIRECTORY'
- ENVIRON_PORT = '389-PORT'
- ENVIRON_SECURE_PORT = '389-SECURE-PORT'
- DEFAULT_PORT_ROOT = str(389)
- DEFAULT_PORT_NON_ROOT = str(1389)
- DEFAULT_SECURE_PORT_ROOT = str(636)
- DEFAULT_SECURE_PORT_NON_ROOT = str(1636)
- DEFAULT_USER = 'nobody'
- DEFAULT_GROUP = 'nobody'
- DEFAULT_ROOT_DN = 'cn=Directory Manager'
- DEFAULT_HOSTNAME = socket.gethostname()
-
- def validate_user(user):
- '''
- If a user is provided it returns its username
- else it returns the current username.
- It checks that the userId or userName exists
-
- :param: user (optional) can be a userName or userId
- :return: userName of the provided user, if none is provided, it returns current user name
- '''
- assert(user)
- if user.isdigit():
- try:
- username = pwd.getpwuid(int(user)).pw_name
- except KeyError:
- raise KeyError('Unknown userId %d' % user)
- return username
- else:
- try:
- pwd.getpwnam(user).pw_uid
- except KeyError:
- raise KeyError('Unknown userName %s' % user)
- return user
-
- def get_default_user():
- user = os.environ.get(ENVIRON_USER, None)
- if not user:
- user = os.getuid()
- return str(user)
- def get_default_group():
- '''
- If a group is provided it returns its groupname
- else it returns the current groupname.
- It checks that the groupId or groupName exists
-
- :param: group (optional) can be a groupName or groupId
- :return: groupName of the provided group, if none is provided, it returns current group name
- '''
- group = os.environ.get(ENVIRON_GROUP, None)
- if not group:
- return pwd.getpwuid(os.getuid()).pw_name
- return group
- def validate_group(group):
- assert(group)
- if str(group).isdigit():
- try:
- groupname = grp.getgrgid(group).gr_name
- return groupname
- except:
- raise KeyError('Unknown groupId %d' % group)
- else:
- try:
- groupname = grp.getgrnam(group).gr_name
- return groupname
- except:
- raise KeyError('Unknown groupName %s' % group)
- def test_get_group():
- try:
- grpname = get_default_group()
- print 'get_group: %s' % grpname
- except:
- raise
- print "Can not find user group"
- pass
- try:
- grpname = get_default_group(group='tbordaz')
- print 'get_group: %s' % grpname
- except:
- raise
- print "Can not find user group"
- pass
- try:
- grpname = get_default_group(group='coucou')
- print 'get_group: %s' % grpname
- except:
- print "Can not find user group coucou"
- pass
- try:
- grpname = get_default_group('thierry')
- print 'get_group: %s' % grpname
- except:
- raise
- print "Can not find user group thierry"
- pass
- try:
- grpname = get_default_group(1000)
- print 'get_group: %s' % grpname
- except:
- raise
- print "Can not find user group 1000"
- pass
- try:
- grpname = get_default_group(20532)
- print 'get_group: %s' % grpname
- except:
- raise
- print "Can not find user group 20532"
- pass
- try:
- grpname = get_default_group(123)
- print 'get_group: %s' % grpname
- except:
- print "Can not find user group 123"
- pass
-
- def get_default_port():
- port = os.environ.get(ENVIRON_PORT, None)
- if port:
- return port
- if os.getuid() == 0:
- return DEFAULT_PORT_ROOT
- else:
- return DEFAULT_PORT_NON_ROOT
- def validate_port(port):
- assert port
- if not port.isdigit() or int(port) <= 0 :
- raise Exception("port number is invalid: %s" % port)
-
- def get_default_directory():
- directory = os.environ.get(ENVIRON_DIRECTORY, None)
- if not directory:
- directory = os.getcwd()
- return directory
- def validate_directory(directory):
- assert directory
- if not os.path.isdir(directory):
- raise Exception("Supplied directory path is not a directory")
-
- if not os.access(directory, os.W_OK):
- raise Exception("Supplied directory is not writable")
- def get_default_serverid():
- serverid = os.environ.get(ENVIRON_SERVERID, None)
- if not serverid:
- serverid = socket.gethostname().split('.')[0]
- return serverid
-
- def validate_serverid(serverid):
- if not serverid:
- raise Exception("Server id is not defined")
- return serverid
-
- def get_inst_dir(serverid):
- assert serverid
- home = os.getenv("HOME")
- inst_initconfig_file = "%s/%s/dirsrv-%s" % (home, INITCONFIGDIR, serverid)
- if not os.path.isfile(inst_initconfig_file):
- raise Exception("%s config file not found" % inst_initconfig_file)
- f = open(inst_initconfig_file, "r")
- for line in f:
- if line.startswith("INST_DIR"):
- inst_dir = line.split("=")[1]
- inst_dir = inst_dir.replace("\r", "")
- inst_dir = inst_dir.replace("\n", "")
- return inst_dir
- def sanity_check():
- if os.getuid() == 0:
- raise Exception("Not tested for root user.. sorry")
-
- home = os.getenv("HOME")
- inst_initconfig_dir = "%s/%s" % (home, INITCONFIGDIR)
- if not os.path.isdir(inst_initconfig_dir):
- raise Exception("Please create the directory \'%s\' and retry." % inst_initconfig_dir )
- class DSadmCmd(object):
- def __init__(self):
- self.version = '0.1'
-
- def _start_subparser(self, subparsers):
- start_parser = subparsers.add_parser(
- 'start',
- help='Start a Directory Server Instance')
- start_parser.add_argument('-I', '--server-id', dest='server_id', type=str, nargs='?',
- metavar='SERVER-ID',
- help='Server Identifier (Default: %s) ' % get_default_serverid())
- start_parser.set_defaults(func=self.start_action)
-
- def _stop_subparser(self, subparsers):
- start_parser = subparsers.add_parser(
- 'stop',
- help='Stop a Directory Server Instance')
- start_parser.add_argument('-I', '--server-id', dest='server_id', type=str, nargs='?',
- metavar='SERVER-ID',
- help='Server Identifier (Default: %s) ' % get_default_serverid())
- start_parser.set_defaults(func=self.stop_action)
-
- def _restart_subparser(self, subparsers):
- start_parser = subparsers.add_parser(
- 'restart',
- help='Retart a Directory Server Instance')
- start_parser.add_argument('-I', '--server-id', dest='server_id', type=str, nargs='?',
- metavar='SERVER-ID',
- help='Server Identifier (Default: %s) ' % get_default_serverid())
- start_parser.set_defaults(func=self.restart_action)
-
- def _delete_subparser(self, subparsers):
- delete_parser = subparsers.add_parser(
- 'delete',
- help='Delete a Directory Server Instance')
- delete_parser.add_argument('-I', '--server-id', dest='server_id', type=str, nargs='?',
- metavar='SERVER-ID',
- help='Server Identifier (Default: %s) ' % get_default_serverid())
- delete_parser.add_argument('-debug', '--debug', dest='debug_level', type=int, nargs='?',
- metavar='DEBUG_LEVEL',
- help='Debug level (Default: 0)')
- delete_parser.set_defaults(func=self.delete_action)
-
- def _create_subparser(self, subparsers):
- create_parser = subparsers.add_parser(
- 'create',
- help='Create a Directory Server Instance')
- create_parser.add_argument('-I', '--server-id', dest='server_id', type=str, nargs='?',
- metavar='SERVER-ID',
- help='Server Identifier (Default: %s) ' % get_default_serverid())
- create_parser.add_argument('-s', '--suffix', dest='suffix', type=str, nargs='?',
- metavar='SUFFIX-DN',
- help='Suffix (Default: create no suffix)')
- create_parser.add_argument('-p', '--port', dest='port', type=int, nargs='?',
- metavar='NON-SECURE-PORT',
- help='Normal Port to listen (Default: %s(root)/%s(non-root)) ' % (DEFAULT_PORT_ROOT, DEFAULT_PORT_NON_ROOT))
-
- create_parser.add_argument('-P', '--secure-port', dest='secure_port', type=int, nargs='?',
- metavar='SECURE-PORT',
- help='Secure Port to listen (Default: %s(root)/%s(non-root))' % (DEFAULT_SECURE_PORT_ROOT, DEFAULT_SECURE_PORT_NON_ROOT))
-
- create_parser.add_argument('-D', '--rootDN', dest='root_dn', type=str, nargs='?',
- metavar='ROOT-DN',
- help='Uses DN as Directory Manager DN (Default: \'%s\')' % (DEFAULT_ROOT_DN))
-
- create_parser.add_argument('-u', '--user-name', dest='user_name', type=str, nargs='?',
- metavar='USER-NAME',
- help='User name of the instance owner (Default: %s)' % DEFAULT_USER)
-
- create_parser.add_argument('-g', '--group-name', dest='group_name', type=str, nargs='?',
- metavar='GROUP-NAME',
- help='Group name of the instance owner (Default: %s)' % DEFAULT_GROUP)
-
- create_parser.add_argument('-d', '--directory-path', dest='directory_path', type=str, nargs='?',
- metavar='DIRECTORY-PATH',
- help='Installation directory path (Default: %s)' % get_default_directory())
- create_parser.add_argument('-debug', '--debug', dest='debug_level', type=int, nargs='?',
- metavar='DEBUG_LEVEL',
- help='Debug level (Default: 0)')
- create_parser.add_argument('-k', '--keep_template', dest='keep_template', type=str, nargs='?',
- help='Keep template file')
-
- create_parser.set_defaults(func=self.create_action)
- #
- # common function for start/stop/restart actions
- #
- def script_action(self, args, script, action_str):
- args = vars(args)
- serverid = args.get('server_id', None)
- if not serverid:
- serverid = get_default_serverid()
-
- script_file = "%s/%s" % (get_inst_dir(serverid), script)
- if not os.path.isfile(script_file):
- raise Exception("%s not found" % script_file)
-
- if not os.access(script_file, os.X_OK):
- raise Exception("%s not executable" % script_file)
- env = os.environ.copy()
- prog = [ script_file ]
- pipe = Popen(prog, cwd=os.getcwd(), env=env,
- stdin=PIPE, stdout=PIPE, stderr=STDOUT)
- child_stdin = pipe.stdin
- child_stdout = pipe.stdout
- for line in child_stdout:
- sys.stdout.write(line)
- child_stdout.close()
- child_stdin.close()
-
- rc = pipe.wait()
- if rc == 0:
- print "Directory %s %s" % (serverid, action_str)
- else:
- print "Failure: directory %s not %s (%s)" % (serverid, action_str, rc)
- return
-
- def start_action(self, args):
- self.script_action(args, SCRIPT_START, "started")
-
-
- def stop_action(self, args):
- self.script_action(args, SCRIPT_STOP, "stopped")
-
- def restart_action(self, args):
- self.script_action(args, SCRIPT_RESTART, "restarted")
- def delete_action(self, args):
- args = vars(args)
- serverid = args.get('server_id', None)
- if not serverid:
- serverid = get_default_serverid()
-
- #prepare the remove-ds options
- debug_level = args.get('debug_level', None)
- if debug_level:
- debug_str = ['-d']
- for i in range(1, int(debug_level)):
- debug_str.append('d')
- debug_str = ''.join(debug_str)
-
- env = os.environ.copy()
- prog = [REMOVE_DS]
- if debug_level:
- prog.append(debug_str)
- prog.append("-i")
- prog.append("slapd-%s" % serverid)
-
- # run the REMOVE_DS command and print the possible output
- pipe = Popen(prog, cwd=os.getcwd(), env=env,
- stdin=PIPE, stdout=PIPE, stderr=STDOUT)
- child_stdin = pipe.stdin
- child_stdout = pipe.stdout
- for line in child_stdout:
- if debug_level:
- sys.stdout.write(line)
- child_stdout.close()
- child_stdin.close()
-
- rc = pipe.wait()
- if rc == 0:
- print "Directory server \'%s\' successfully deleted" % serverid
- else:
- print "Fail to delete directory \'%s\': %d" % (serverid, rc)
- return
- #
- # used by create subcommand to build the template file
- #
- def _create_setup_ds_file(self, args, user=None, group=None):
- # Get/checks the argument with the following order
- # - parameter
- # - Environment
- # - default
- serverid = args.get('server_id', None)
- if not serverid:
- serverid = get_default_serverid()
- serverid = validate_serverid(serverid)
-
- username = args.get('user_name', None)
- if not username:
- username = get_default_user()
- username = validate_user(username)
-
- groupname = args.get('group_name', None)
- if not groupname:
- groupname = get_default_group()
- groupname = validate_group(groupname)
-
- directoryname = args.get('directory_path', None)
- if not directoryname:
- directoryname = get_default_directory()
- validate_directory(directoryname)
-
- portnumber = args.get('port', None)
- if not portnumber:
- portnumber = get_default_port()
- validate_port(portnumber)
-
- suffix = args.get('suffix', None)
- tempf = tempfile.NamedTemporaryFile(delete=False)
- tempf.write('[General]\n')
- tempf.write('FullMachineName=%s\n' % DEFAULT_HOSTNAME)
- tempf.write('SuiteSpotUserID=%s\n' % username)
- tempf.write('SuiteSpotGroup=%s\n' % groupname)
- tempf.write('ServerRoot=%s\n' % directoryname)
- tempf.write('\n')
- tempf.write('[slapd]\n')
- tempf.write('ServerPort=1389\n')
- tempf.write('ServerIdentifier=%s\n' % serverid)
- if suffix:
- tempf.write('Suffix=%s\n' % suffix)
- tempf.write('RootDN=cn=Directory Manager\n')
- tempf.write('RootDNPwd=Secret12\n')
- tempf.write('sysconfdir=%s/etc\n' % directoryname)
- tempf.write('localstatedir=%s/var\n' % directoryname)
- tempf.write('inst_dir=%s/lib/dirsrv/slapd-%s\n'% (directoryname, serverid))
- tempf.write('config_dir=%s/etc/dirsrv/slapd-%s\n' % (directoryname, serverid))
- tempf.close()
-
- keep_template = args.get('keep_template', None)
- if keep_template:
- shutil.copy(tempf.name, keep_template)
-
- return tempf
- #
- # It silently creates an instance.
- # After creation the instance is started
- #
- def create_action(self, args):
- args = vars(args)
-
- # retrieve the serverid here just to log the final status
- serverid = args.get('server_id', None)
- if not serverid:
- serverid = get_default_serverid()
- # prepare the template file
- tempf = self._create_setup_ds_file(args)
- #prepare the setup-ds options
- debug_level = args.get('debug_level', None)
- if debug_level:
- debug_str = ['-d']
- for i in range(1, int(debug_level)):
- debug_str.append('d')
- debug_str = ''.join(debug_str)
- #
- # run the SETUP_DS command and print the possible output
- #
- env = os.environ.copy()
- prog = [SETUP_DS]
- if debug_level:
- prog.append(debug_str)
- prog.append("--silent")
- prog.append("--file=%s" % tempf.name)
- tempf.close()
- pipe = Popen(prog, cwd=os.getcwd(), env=env,
- stdin=PIPE, stdout=PIPE, stderr=STDOUT)
- child_stdin = pipe.stdin
- child_stdout = pipe.stdout
- for line in child_stdout:
- if debug_level:
- sys.stdout.write(line)
- child_stdout.close()
- child_stdin.close()
- os.unlink(tempf.name)
- rc = pipe.wait()
- if rc == 0:
- print "Directory server \'%s\' successfully created" % serverid
- else:
- print "Fail to create directory \'%s\': %d" % (serverid, rc)
- return
- #
- # parser of the main command. It contains subcommands
- #
- def get_parser(self, argv):
-
- parser = argparse.ArgumentParser(
- description='Managing a local directory server instance')
-
- subparsers = parser.add_subparsers(
- metavar='SUBCOMMAND',
- help='The action to perform')
- #pdb.set_trace()
- # subcommands
- self._create_subparser(subparsers)
- self._delete_subparser(subparsers)
- self._start_subparser(subparsers)
- self._stop_subparser(subparsers)
- self._restart_subparser(subparsers)
- # Sanity check that the debug level is valid
- args = vars(parser.parse_args(argv))
- debug_level = args.get('debug_level', None)
- if debug_level and (int(debug_level) < 1 or int(debug_level > 5)):
- raise Exception("invalid debug level: range 1..5")
- return parser
-
- def main(self, argv):
- sanity_check()
- parser = self.get_parser(argv)
- args = parser.parse_args(argv)
- args.func(args)
- return
- if __name__ == '__main__':
- DSadmCmd().main(sys.argv[1:])
|