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 from Bcfg2.Compat import input # default config file config = '''[server] repository = %s plugins = %s [statistics] sendmailpath = %s #web_debug = False #time_zone = [database] #engine = sqlite3 # 'postgresql', 'mysql', 'mysql_old', 'sqlite3' or 'ado_mssql'. #name = # Or path to database file if using sqlite3. #/bcfg2.sqlite is default path if left empty #user = # Not used with sqlite3. #password = # Not used with sqlite3. #host = # Not used with sqlite3. #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'), ('Arch', 'arch')] # 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', 'TCheetah', 'TGenshi'] # Default list of plugins to use default_plugins = Bcfg2.Options.SERVER_PLUGINS.default 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 = 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 = 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 = 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 = input("Location of Bcfg2 repository [%s]: " % self.repopath) if newrepo != '': self.repopath = os.path.abspath(newrepo) if os.path.isdir(self.repopath): response = 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 = 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(input(prompt)) self.os_sel = os_list[osidx - 1][1] break except ValueError: continue def _prompt_plugins(self): default = input("Use default plugins? (%s) [Y/n]: " % ''.join(default_plugins)).lower() if default != 'y' or default != '': while True: plugins_are_valid = True plug_str = 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 = input("Country name (2 letter code) for certificate: ") if newcountry != '': if len(newcountry) == 2: self.country = newcountry else: while len(newcountry) != 2: newcountry = input("2 letter country code (eg. US): ") if len(newcountry) == 2: self.country = newcountry break else: self.country = 'US' newstate = input("State or Province Name (full name) for certificate: ") if newstate != '': self.state = newstate else: self.state = 'Illinois' newlocation = 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_xml=groups % self.os_sel, clients_xml=clients % socket.getfqdn()) 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)