From dab1d03d81c538966d03fb9318a4588a9e803b44 Mon Sep 17 00:00:00 2001 From: Sol Jerome Date: Sat, 24 Mar 2012 11:20:07 -0500 Subject: Allow to run directly from a git checkout (#1037) Signed-off-by: Sol Jerome --- src/lib/Bcfg2/Server/Admin/Init.py | 357 +++++++++++++++++++++++++++++++++++++ 1 file changed, 357 insertions(+) create mode 100644 src/lib/Bcfg2/Server/Admin/Init.py (limited to 'src/lib/Bcfg2/Server/Admin/Init.py') diff --git a/src/lib/Bcfg2/Server/Admin/Init.py b/src/lib/Bcfg2/Server/Admin/Init.py new file mode 100644 index 000000000..832190b7d --- /dev/null +++ b/src/lib/Bcfg2/Server/Admin/Init.py @@ -0,0 +1,357 @@ +import getpass +import os +import random +import socket +import stat +import string +import sys +import subprocess +import Bcfg2.Server.Admin +import Bcfg2.Server.Plugin +import Bcfg2.Options + +# default config file +config = ''' +[server] +repository = %s +plugins = %s + +[statistics] +sendmailpath = %s +database_engine = sqlite3 +# 'postgresql', 'mysql', 'mysql_old', 'sqlite3' or 'ado_mssql'. +database_name = +# Or path to database file if using sqlite3. +#/etc/brpt.sqlite is default path if left empty +database_user = +# Not used with sqlite3. +database_password = +# Not used with sqlite3. +database_host = +# Not used with sqlite3. +database_port = + +[communication] +protocol = %s +password = %s +certificate = %s +key = %s +ca = %s + +[components] +bcfg2 = %s +''' + +# Default groups +groups = ''' + + + + + + + + + + + + +''' + +# Default contents of clients.xml +clients = ''' + + +''' + +# Mapping of operating system names to groups +os_list = [('Red Hat/Fedora/RHEL/RHAS/Centos', 'redhat'), + ('SUSE/SLES', 'suse'), + ('Mandrake', 'mandrake'), + ('Debian', 'debian'), + ('Ubuntu', 'ubuntu'), + ('Gentoo', 'gentoo'), + ('FreeBSD', 'freebsd')] + +# Complete list of plugins +plugin_list = ['Account', + 'Base', + 'Bundler', + 'Bzr', + 'Cfg', + 'Decisions', + 'Deps', + 'Git', + 'Guppy', + 'Hg', + 'Metadata', + 'NagiosGen', + 'Ohai', + 'Packages', + 'Pkgmgr', + 'Probes', + 'Properties', + 'Rules', + 'Snapshots', + 'SSHbase', + 'SSLCA', + 'Statistics', + 'Svcmgr', + 'TCheetah', + 'TGenshi'] + +# Default list of plugins to use +default_plugins = Bcfg2.Options.SERVER_PLUGINS.default + + +def get_input(prompt): + """py3k compatible function to get input""" + try: + return raw_input(prompt) + except NameError: + return input(prompt) + + +def gen_password(length): + """Generates a random alphanumeric password with length characters.""" + chars = string.letters + string.digits + newpasswd = '' + for i in range(length): + newpasswd = newpasswd + random.choice(chars) + return newpasswd + + +def create_key(hostname, keypath, certpath, country, state, location): + """Creates a bcfg2.key at the directory specifed by keypath.""" + kcstr = ("openssl req -batch -x509 -nodes -subj '/C=%s/ST=%s/L=%s/CN=%s' " + "-days 1000 -newkey rsa:2048 -keyout %s -noout" % (country, + state, + location, + hostname, + keypath)) + subprocess.call((kcstr), shell=True) + ccstr = ("openssl req -batch -new -subj '/C=%s/ST=%s/L=%s/CN=%s' -key %s " + "| openssl x509 -req -days 1000 -signkey %s -out %s" % (country, + state, + location, + hostname, + keypath, + keypath, + certpath)) + subprocess.call((ccstr), shell=True) + os.chmod(keypath, stat.S_IRUSR | stat.S_IWUSR) # 0600 + + +def create_conf(confpath, confdata, keypath): + # Don't overwrite existing bcfg2.conf file + if os.path.exists(confpath): + result = get_input("\nWarning: %s already exists. " + "Overwrite? [y/N]: " % confpath) + if result not in ['Y', 'y']: + print("Leaving %s unchanged" % confpath) + return + try: + open(confpath, "w").write(confdata) + os.chmod(confpath, stat.S_IRUSR | stat.S_IWUSR) # 0600 + except Exception: + e = sys.exc_info()[1] + print("Error %s occured while trying to write configuration " + "file to '%s'.\n" % + (e, confpath)) + raise SystemExit(1) + + +class Init(Bcfg2.Server.Admin.Mode): + __shorthelp__ = ("Interactively initialize a new repository.") + __longhelp__ = __shorthelp__ + "\n\nbcfg2-admin init" + __usage__ = "bcfg2-admin init" + options = {'configfile': Bcfg2.Options.CFILE, + 'plugins': Bcfg2.Options.SERVER_PLUGINS, + 'proto': Bcfg2.Options.SERVER_PROTOCOL, + 'repo': Bcfg2.Options.SERVER_REPOSITORY, + 'sendmail': Bcfg2.Options.SENDMAIL_PATH} + repopath = "" + response = "" + + def _set_defaults(self): + """Set default parameters.""" + self.configfile = self.opts['configfile'] + self.repopath = self.opts['repo'] + self.password = gen_password(8) + self.server_uri = "https://%s:6789" % socket.getfqdn() + self.plugins = default_plugins + + def __call__(self, args): + Bcfg2.Server.Admin.Mode.__call__(self, args) + + # Parse options + self.opts = Bcfg2.Options.OptionParser(self.options) + self.opts.parse(args) + self._set_defaults() + + # Prompt the user for input + self._prompt_config() + self._prompt_repopath() + self._prompt_password() + self._prompt_hostname() + self._prompt_server() + self._prompt_groups() + # self._prompt_plugins() + self._prompt_certificate() + + # Initialize the repository + self.init_repo() + + def _prompt_hostname(self): + """Ask for the server hostname.""" + data = get_input("What is the server's hostname [%s]: " % + socket.getfqdn()) + if data != '': + self.shostname = data + else: + self.shostname = socket.getfqdn() + + def _prompt_config(self): + """Ask for the configuration file path.""" + newconfig = get_input("Store Bcfg2 configuration in [%s]: " % + self.configfile) + if newconfig != '': + self.configfile = os.path.abspath(newconfig) + + def _prompt_repopath(self): + """Ask for the repository path.""" + while True: + newrepo = get_input("Location of Bcfg2 repository [%s]: " % + self.repopath) + if newrepo != '': + self.repopath = os.path.abspath(newrepo) + if os.path.isdir(self.repopath): + response = get_input("Directory %s exists. Overwrite? [y/N]:" \ + % self.repopath) + if response.lower().strip() == 'y': + break + else: + break + + def _prompt_password(self): + """Ask for a password or generate one if none is provided.""" + newpassword = getpass.getpass( + "Input password used for communication verification " + "(without echoing; leave blank for a random): ").strip() + if len(newpassword) != 0: + self.password = newpassword + + def _prompt_server(self): + """Ask for the server name.""" + newserver = get_input("Input the server location [%s]: " % + self.server_uri) + if newserver != '': + self.server_uri = newserver + + def _prompt_groups(self): + """Create the groups.xml file.""" + prompt = '''Input base Operating System for clients:\n''' + for entry in os_list: + prompt += "%d: %s\n" % (os_list.index(entry) + 1, entry[0]) + prompt += ': ' + while True: + try: + osidx = int(get_input(prompt)) + self.os_sel = os_list[osidx - 1][1] + break + except ValueError: + continue + + def _prompt_plugins(self): + default = get_input("Use default plugins? (%s) [Y/n]: " % + ''.join(default_plugins)).lower() + if default != 'y' or default != '': + while True: + plugins_are_valid = True + plug_str = get_input("Specify plugins: ") + plugins = plug_str.split(',') + for plugin in plugins: + plugin = plugin.strip() + if not plugin in plugin_list: + plugins_are_valid = False + print("ERROR: Plugin %s not recognized" % plugin) + if plugins_are_valid: + break + + def _prompt_certificate(self): + """Ask for the key details (country, state, and location).""" + print("The following questions affect SSL certificate generation.") + print("If no data is provided, the default values are used.") + newcountry = get_input("Country name (2 letter code) for certificate: ") + if newcountry != '': + if len(newcountry) == 2: + self.country = newcountry + else: + while len(newcountry) != 2: + newcountry = get_input("2 letter country code (eg. US): ") + if len(newcountry) == 2: + self.country = newcountry + break + else: + self.country = 'US' + + newstate = get_input("State or Province Name (full name) for certificate: ") + if newstate != '': + self.state = newstate + else: + self.state = 'Illinois' + + newlocation = get_input("Locality Name (eg, city) for certificate: ") + if newlocation != '': + self.location = newlocation + else: + self.location = 'Argonne' + + def _init_plugins(self): + """Initialize each plugin-specific portion of the repository.""" + for plugin in self.plugins: + if plugin == 'Metadata': + Bcfg2.Server.Plugins.Metadata.Metadata.init_repo(self.repopath, + groups, + self.os_sel, + clients) + else: + try: + module = __import__("Bcfg2.Server.Plugins.%s" % plugin, '', + '', ["Bcfg2.Server.Plugins"]) + cls = getattr(module, plugin) + cls.init_repo(self.repopath) + except Exception: + e = sys.exc_info()[1] + print("Plugin setup for %s failed: %s\n" + "Check that dependencies are installed?" % (plugin, e)) + + def init_repo(self): + """Setup a new repo and create the content of the configuration file.""" + keypath = os.path.dirname(self.configfile) + kpath = os.path.join(keypath, 'bcfg2.key') + cpath = os.path.join(keypath, 'bcfg2.crt') + + confdata = config % (self.repopath, + ','.join(self.plugins), + self.opts['sendmail'], + self.opts['proto'], + self.password, + cpath, + kpath, + cpath, + self.server_uri) + + # Create the configuration file and SSL key + create_conf(self.configfile, confdata, keypath) + create_key(self.shostname, kpath, cpath, self.country, + self.state, self.location) + + # Create the repository + path = os.path.join(self.repopath, 'etc') + try: + os.makedirs(path) + self._init_plugins() + print("Repository created successfuly in %s" % (self.repopath)) + except OSError: + print("Failed to create %s." % path) -- cgit v1.2.3-1-g7c22