def chmod(location, mode=None, owner=None, group=None, recursive=False): if mode: run('chmod %s %s %s' % (recursive and '-R ' or '', mode, location)) if owner: chown(location, owner=owner, group=group, recursive=recursive) elif group: chgrp(location, group, recursive=recursive)
def configure(): """ Configure PureFTP """ with sudo(): # Echo configurations setup_config() for user in blueprint.get('users'): username, password = user['username'], user['password'] if 'homedir' in user: user_home = user['homedir'] else: user_home = os.path.join(ftp_root, username) passwd_path = '/etc/pure-ftpd/pureftpd.passwd' with settings(warn_only=True): if files.exists(passwd_path) and run('pure-pw show {}'.format( username)).return_code == 0: continue debian.mkdir(user_home, owner=ftp_user, group=ftp_group) prompts = { 'Password: '******'Enter it again: ': password } with settings(prompts=prompts): run('pure-pw useradd {} -u {} -g {} -d {}'.format(username, ftp_user, ftp_group, user_home)) run('pure-pw mkdb') restart()
def install(): with sudo(): debian.apt_get('install', 'pure-ftpd', 'openssl') # Create ftp user user.create_service_user(ftp_user, groups=[ftp_group]) # Create ftp root dir debian.mkdir(ftp_root, mode=1770, owner=ftp_user, group=ftp_group) # Set up symlinks debian.ln('/etc/pure-ftpd/conf/PureDB', '/etc/pure-ftpd/auth/PureDB') # Enable TLS run('echo 1 > /etc/pure-ftpd/conf/TLS') key_path = '/etc/ssl/private/pure-ftpd.pem' if not files.exists(key_path): prompts = { 'Country Name (2 letter code) [AU]:': '', 'State or Province Name (full name) [Some-State]:': '', 'Locality Name (eg, city) []:': '', 'Organization Name (eg, company) [Internet Widgits Pty Ltd]:': '', 'Organizational Unit Name (eg, section) []:': '', 'Common Name (e.g. server FQDN or YOUR name) []:': '', 'Email Address []:': '' } with settings(prompts=prompts): run('openssl req -x509 -nodes -newkey rsa:2048 -keyout {0} -out {0}'.format( key_path)) debian.chmod(key_path, 600)
def locale_gen(locale, utf8=True): locale_gen_cmd = 'locale-gen {}'.format(locale) locale_gen_utf8_cmd = locale_gen_cmd + '.UTF-8' with sudo(): run(locale_gen_cmd) if utf8: run(locale_gen_utf8_cmd)
def add_apt_repository(repository, accept=True, src=False): if src: run('add-apt-repository "{}" {}'.format(repository, '--yes' if accept else '')) else: # Add repository manually to sources.list due to ubuntu bug if not repository.startswith('deb '): repository = 'deb {}'.format(repository) fabric.contrib.files.append('/etc/apt/sources.list', repository, shell=True)
def set_timezone(timezone): """ Set OS timezone :param timezone: Europe/Stockholm """ with silent(): run('ln -sf /usr/share/zoneinfo/{} /etc/localtime'.format(timezone))
def unmount(mount_point): """ Unmount mount point. :param str mount_point: Name of mount point to unmount """ with sudo(), silent(): info('Unmounting {}', mount_point) run('umount {}'.format(mount_point))
def install(install_java=True): with sudo(): if install_java: from blues import java java.install() version = blueprint.get('version', '0.13.3') info('Downloading Metabase v%s' % version) run('mkdir -p /etc/metabase/ && cd /etc/metabase/ && curl -O ' 'http://downloads.metabase.com/v%s/metabase.jar' % version)
def status(user=None): """ Dumps the crontab for a single user """ if not user: abort('Please specify user account') info('Current crontab for {}:', user) with sudo(), hide_prefix(): run('crontab -l -u {}'.format(user))
def build(self): # This is necessary since the app blueprint doesn't care about # package.json change detection and handling. self.install_requirements() with sudo_project(), cd(git_repository_path()), bash_profile(), \ prefix('export STATIC_BASE={}'.format(static_base())): run('gulp build') debian.chgrp(static_base(), group='www-data', recursive=True)
def disable(user=None): """ Removes the crontab for a single user """ if not user: abort('Please specify user account') with sudo(): info('Disabling crontab for {}...', user) run('crontab -r -u {}'.format(user))
def configure(): """ Install crontab per termplate (user) """ with sudo(), silent(): with debian.temporary_dir(mode=555) as temp_dir: updates = blueprint.upload('./', temp_dir) for update in updates: user = os.path.basename(update) info('Installing new crontab for {}...', user) run('crontab -u {} {}'.format(user, os.path.join(temp_dir, user)))
def kill(sig, process, use_pkill=False): with sudo(): with silent('warnings'): if use_pkill: output = run('pkill -{} {}'.format(sig, process)) else: output = run('kill -{} {}'.format(sig, process)) if output.return_code != 0: warn('No process got {} signal'.format(sig)) else: info('Successfully sent {} signal to {}', sig, process)
def list(*values): """ List sysctl values, e.g. vm.swappiness,vm.panic_on_oom' """ with sudo(), silent(): if not values: for key in run('sysctl -a').split('\n'): info(key) else: for value in values: info(run('sysctl %s' % value))
def grep(needle, haystack, flags="i"): """ Basic file grepping, case-insensitive by default """ if not needle and haystack: abort('Missing arguments') with hide_prefix(), sudo(): run('grep{} -e "{}" {}'.format( ' -{}'.format(flags) if flags else '', needle, haystack))
def configure(): """ Install incrontab per template (i.e. user) """ with sudo(), silent(): updates = blueprint.upload('./', '/etc') users = [os.path.basename(update) for update in updates] put(StringIO('\n'.join(users)), '/etc/incron.allow', use_sudo=True) for user in users: info('Installing new incrontab for {}...', user) run('incrontab -u {} {}'.format(user, os.path.join('/etc/incron.usertables', user)))
def page_size(): """ Get PAGE_SIZE """ c = fabric.context_managers with c.settings(c.hide('running', 'stdout')): return int(run('getconf PAGE_SIZE').strip())
def groupadd(name, gid=None, gid_min=None, gid_max=None, system=False): group = get_group(name) if not group: options = [] if gid: options.append("-g '%s'" % gid) if gid_min: options.append("-K GID_MIN='%s'" % gid_min) if gid_max: options.append("-K GID_MAX='%s'" % gid_max) if system: options.append('-r') run("groupadd %s '%s'" % (' '.join(options), name)) else: if gid is not None and group.get('gid') != gid: groupmod(name, gid)
def phys_pages(): """ Get _PHYS_PAGES """ c = fabric.context_managers with c.settings(c.hide('running', 'stdout')): return int(run('getconf _PHYS_PAGES').strip())
def add_fstab(filesystem=None, mount_point=None, type='auto', options='rw', dump='0', pazz='0'): """ Add mount point configuration to /etc/fstab. If mount point already mounted on different file system then unmount. :param str filesystem: The partition or storage device to be mounted :param str mount_point: The mount point where <filesystem> is mounted to :param str type: The file system type (Default: auto) :param str options: Mount options of the filesystem (Default: rw) :param str dump: Used by the dump utility to decide when to make a backup, 0|1 (Default: 0) :param str pazz: Used by fsck to decide which order filesystems are to be checked (Default: 0) """ with sudo(): fstab_line = '{fs} {mount} {type} {options} {dump} {pazz}'.format( fs=filesystem, mount=mount_point, type=type, options=options, dump=dump, pazz=pazz ) validate_boot_options(options) # Add mount to /etc/fstab if not already there (?) with silent(): output = run('cat /etc/fstab') fstab = output.stdout if fstab_line not in fstab.split('\n'): # TODO: Handle comments info('Adding fstab: {} on {}', filesystem, mount_point) fabric.contrib.files.append('/etc/fstab', fstab_line, use_sudo=True) # Unmount any previous mismatching mount point mounted_file_system = get_mount(mount_point) if mounted_file_system and mounted_file_system != filesystem: unmount(mount_point)
def get_group(name): group_data = run("cat /etc/group | egrep '^%s:' ; true" % name) if group_data: name, _, gid, members = group_data.split(':', 4) return dict(name=name, gid=gid, members=tuple(m.strip() for m in members.split(',') if m.strip())) else: return None
def nproc(): """ Get the number of CPU cores. """ c = fabric.context_managers with c.settings(c.hide('running', 'stdout')): res = run('nproc').strip() return int(res)
def service(name, action, check_status=True, show_output=False): c = fabric.context_managers with sudo('root'), c.settings(c.hide('running', 'stdout', 'stderr', 'warnings'), warn_only=True): info('Service: {} {}', name, action) if check_status: output = run('service {} status'.format(name), pty=False, combine_stderr=True) if output.return_code != 0: puts(indent(magenta(output))) return elif action in output: puts(indent('...has status {}'.format(magenta(output[len(name)+1:])))) return output = run('service {} {}'.format(name, action), pty=False, combine_stderr=True) if output.return_code != 0 or show_output: puts(indent(magenta(output)))
def get_user(name): with silent(): d = run("cat /etc/passwd | egrep '^%s:' ; true" % name, user='******') s = run("cat /etc/shadow | egrep '^%s:' | awk -F':' '{print $2}'" % name, user='******') results = {} if d: d = d.split(':') assert len(d) >= 7, "/etc/passwd entry is expected to have at least 7 fields, " \ "got %s in: %s" % (len(d), ':'.join(d)) results = dict(name=d[0], uid=d[2], gid=d[3], home=d[5], shell=d[6]) if s: results['passwd'] = s if results: return results else: return None
def install(): """ Install wkhtmltox from the pkgs on sourceforge that are compiled with patched QT. This version doesn't need X/Xvfb to run. """ # Can't be named version since it'll conflict with fabrics own version variable wkhtmltox_ver = blueprint.get('wkhtmltopdf_version', '0.12.2.1') wkhtmltox_pkg = 'wkhtmltox-{}_linux-{}-amd64.deb'.format( wkhtmltox_ver, debian.lbs_codename()) wkhtmltox_url = 'http://downloads.sourceforge.net/project/wkhtmltopdf/{}/{}'.format( wkhtmltox_ver, wkhtmltox_pkg) run('curl --silent --location --show-error --remote-name "{}"'.format( wkhtmltox_url)) with sudo(): with settings(warn_only=True): run('dpkg -i {}'.format(wkhtmltox_pkg)) debian.apt_get('--fix-broken', 'install') debian.rm(wkhtmltox_pkg)
def configure(): """ Install crontab per template (i.e. user) """ with sudo(), silent(): with debian.temporary_dir(mode=555) as temp_dir: updates = blueprint.upload('./', temp_dir) for user, schedule in blueprint.get('', {}).items(): tmp_file = os.path.join(temp_dir, user) blueprint.upload('./crontab', tmp_file, context={"schedule": schedule}) updates.append(user) for update in updates: if update.endswith('crontab'): continue user = os.path.basename(update) info('Installing new crontab for {}...', user) run('crontab -u {} {}'.format(user, os.path.join(temp_dir, user)))
def usermod(user, password=None, home=None, uid=None, gid=None, groups=None, shell=None): if isinstance(user, basestring): user = get_user(user) options = [] if home is not None and user.get('home') != home: options.append("-d '%s'" % home) if uid is not None and user.get('uid') != uid: options.append("-u '%s'" % uid) if gid is not None and user.get('gid') != gid: options.append("-g '%s'" % gid) if groups: options.append("-a -G '%s'" % ','.join(groups)) if shell is not None and user.get('shell') != shell: options.append("-s '%s'" % shell) if options: run("usermod %s '%s'" % (' '.join(options), user['name'])) if password: chpasswd(user['name'], password)
def total_memory(): """ Get total memory in bytes """ c = fabric.context_managers with c.settings(c.hide('running', 'stdout')): memory = int(run("grep MemTotal /proc/meminfo | awk '{print $2}'")) # Convert to bytes memory *= 1024 return memory
def mkdir(location, recursive=True, mode=None, owner=None, group=None): with silent(), sudo(): result = run('test -d "%s" || mkdir %s %s "%s"' % (location, mode and '-m %s' % mode or '', recursive and '-p' or '', location)) if result.succeeded: if owner or group: chmod(location, owner=owner, group=group) else: raise Exception('Failed to create directory %s, %s' % (location, result.stdout))
def set_password(old_password='******', user='******'): """ Sets Neo4j password """ new_password = blueprint.get('password') assert new_password info("Checking password") output = run( 'curl -u %s:%s http://localhost:7474/user/%s' % ( user, new_password, user)) if '"username" : "%s"' % user in output: info("Password already set") else: if 'AuthorizationFailed' in output: info("Waiting 5 sec due to Jetty's bruteforce protection") time.sleep(5) info("Setting password") assert old_password # escape before sending in json via cli new_password = new_password.replace('\\', '\\\\') new_password = new_password.replace('"', '\\"') new_password = new_password.replace("'", "\\'") output = run( 'curl -u %s:%s -X POST http://localhost:7474/user/%s/password ' '-H "Accept: application/json; charset=UTF-8" ' '-H "Content-Type: application/json" ' '-d \'{"password" : "%s"}\' ' % ( user, old_password, user, new_password)) if 'AuthorizationFailed' in output: abort("Wrong current Neo4j password, cannot change it") elif '"username" : "%s"' % user not in output: abort("Unexpected response")
def mount(mount_point, owner=None, group=None, **fstab): """ Mount and optionally add configuration to fstab. :param str mount_point: Name of mount point :param str owner: Name of mount point owner :param str group: Name of mount point group :param dict fstab: Optional kwargs passed to add_fstab() """ with sudo(): if fstab: add_fstab(mount_point=mount_point, **fstab) # Mount if not is_mounted(mount_point): # Ensure mount point dir exists mkdir(mount_point, owner=owner, group=group, mode=755) with silent(): info('Mounting {}', mount_point) run('mount {}'.format(mount_point))
def mktemp(directory=False, mode=None): with silent(), sudo(): cmd = 'mktemp' if directory: cmd += ' -d' output = run(cmd) path = output.stdout if directory: path += os.path.sep if mode: chmod(path, mode=mode) return path
def configure(): """ Configure PureFTP """ with sudo(): # Echo configurations setup_config() for user in blueprint.get('users'): username, password = user['username'], user['password'] passwd_path = '/etc/pure-ftpd/pureftpd.passwd' if files.exists(passwd_path) and run( 'pure-pw show {}'.format(username)).return_code == 0: continue user_home = os.path.join(ftp_root, username) debian.mkdir(user_home, owner=ftp_user, group=ftp_group) prompts = {'Password: '******'Enter it again: ': password} with settings(prompts=prompts): run('pure-pw useradd {} -u {} -g {} -d {}'.format( username, ftp_user, ftp_group, user_home)) run('pure-pw mkdb') restart()
def get_mount(mount_point): """ Resolve a mount point to mounted file system. If not mounted, return None. :param str mount_point: Name of mount point to reslve :return str: Mounted file system """ with silent('warnings'): output = run('egrep ".+ {} .+" /proc/mounts'.format(mount_point)) if output.return_code == 0: file_system = output.stdout.split()[0] return file_system
def install(): with sudo(): debian.apt_get('install', 'pure-ftpd', 'openssl') # Create ftp user debian.useradd(ftp_user, '/dev/null', shell='/bin/false', user_group=True, groups=[ftp_group], uid_min=1000) # Create ftp root dir debian.mkdir(ftp_root, mode=1770, owner=ftp_user, group=ftp_group) # Set up symlinks debian.ln('/etc/pure-ftpd/conf/PureDB', '/etc/pure-ftpd/auth/PureDB') # Enable TLS run('echo 1 > /etc/pure-ftpd/conf/TLS') key_path = '/etc/ssl/private/pure-ftpd.pem' if not files.exists(key_path): prompts = { 'Country Name (2 letter code) [AU]:': '', 'State or Province Name (full name) [Some-State]:': '', 'Locality Name (eg, city) []:': '', 'Organization Name (eg, company) [Internet Widgits Pty Ltd]:': '', 'Organizational Unit Name (eg, section) []:': '', 'Common Name (e.g. server FQDN or YOUR name) []:': '', 'Email Address []:': '' } with settings(prompts=prompts): run('openssl req -x509 -nodes -newkey rsa:2048 -keyout {0} -out {0}' .format(key_path)) debian.chmod(key_path, 600)
def add_fstab(filesystem=None, mount_point=None, type='auto', options='rw', dump='0', pazz='0'): """ Add mount point configuration to /etc/fstab. If mount point already mounted on different file system then unmount. :param str filesystem: The partition or storage device to be mounted :param str mount_point: The mount point where <filesystem> is mounted to :param str type: The file system type (Default: auto) :param str options: Mount options of the filesystem (Default: rw) :param str dump: Used by the dump utility to decide when to make a backup, 0|1 (Default: 0) :param str pazz: Used by fsck to decide which order filesystems are to be checked (Default: 0) """ with sudo(): fstab_line = '{fs} {mount} {type} {options} {dump} {pazz}'.format( fs=filesystem, mount=mount_point, type=type, options=options, dump=dump, pazz=pazz) # Add mount to /etc/fstab if not already there (?) with silent(): output = run('cat /etc/fstab') fstab = output.stdout if fstab_line not in fstab.split('\n'): # TODO: Handle comments info('Adding fstab: {} on {}', filesystem, mount_point) fabric.contrib.files.append('/etc/fstab', fstab_line, use_sudo=True) # Unmount any previous mismatching mount point mounted_file_system = get_mount(mount_point) if mounted_file_system and mounted_file_system != filesystem: unmount(mount_point)
def useradd(name, home=None, create_home=False, shell=None, uid=None, uid_min=None, uid_max=None, user_group=False, gid=None, groups=None, system=False, password=None): """ Create a new user or update default new user information :param name: The username :param home: Home directory of the new account. Will not create dir. *(Optional)* :param create_home: Create the user's home directory *(Default: False)* :param shell: Login shell of the new account *(Optional)* :param user_group: Create a group with the same name as the user *(Default: False)* :param uid: User ID of the new account *(Optional)* :param uid_min: Min value for automatic user/group ID selection *(Optional)* :param uid_max: Max value for automatic user/group ID selection *(Optional)* :param gid: Name or ID of the primary group of the new account *(Optional)* :param groups: List of supplementary groups of the new account *(Optional)* :param system: Create a system account *(Optional)* :param password: Encrypted password of the new account *(Optional)* """ user = get_user(name) if not user: options = [] if home: options.append( "-d '%s'" % home) # home directory of the new account (will not create) if create_home: options.append('-m') # create the user's home directory else: options.append("-M") # do not create the user's home directory if system: options.append('-r') # create a system account if uid: options.append("-u '%s'" % uid) # user ID of the new account if groups: options.append("-G '%s'" % ','.join(groups) ) # list of supplementary groups of the new account if user_group: options.append( "-U") # create a group with the same name as the user if not gid and get_group(name): gid = name # Automatically set gid to user name if the group already exists, otherwise useradd fails. else: options.append( "-N") # do not create a group with the same name as the user if gid: options.append( "-g '%s'" % gid) # name or ID of the primary group of the new account if shell: options.append("-s '%s'" % shell) # login shell of the new account if uid_min: options.append( "-K UID_MIN='%s'" % uid_min) # Min value for automatic user ID selection if user_group: options.append( "-K GID_MIN='%s'" % uid_min) # Min value for automatic group ID selection if uid_max: options.append( "-K UID_MAX='%s'" % uid_max) # Max value for automatic user ID selection if user_group: options.append( "-K GID_MAX='%s'" % uid_max) # Max value for automatic group ID selection if password: options.append("-p %s" % password) # encrypted password of the new account # Create the user run("useradd {options} '{username}'".format(options=' '.join(options), username=name)) else: usermod(user, password=password, home=home, uid=uid, gid=gid, groups=groups, shell=shell)
def cp(source, destination, force=True, mode=None, owner=None, group=None): force = force and '-f' or '' run('cp %s %s %s' % (force, source, destination)) chmod(destination, mode, owner, group)
def set_pureftp_config_value(**kwargs): for key, value in kwargs.iteritems(): run("echo '{}' > /etc/pure-ftpd/conf/{}".format(value, key))
def groupmod(name, gid): run("groupmod -g %s '%s'" % (gid, name))
def command_exists(*command): with fabric.context_managers.quiet(): return all( (run("which '%s' >& /dev/null" % c).succeeded for c in command))
def chown(location, owner, group=None, recursive=False): owner = '{}:{}'.format(owner, group) if group else owner run('chown {} {} {}'.format(recursive and '-R ' or '', owner, location))
def mv(source, destination, force=True): force = force and '-f' or '' run('mv %s %s %s' % (force, source, destination))
def chpasswd(name, password, encrypted_passwd=False): with silent(): encoded_password = base64.b64encode('%s:%s' % (name, password)) encryption = ' -e' if encrypted_passwd else '' run('echo %s | base64 --decode | chpasswd%s' % encoded_password, encryption)
def pwd(): with silent(): return run('pwd').stdout.strip()
def chgrp(location, group, recursive=False): run('chgrp %s %s %s' % (recursive and '-R ' or '', group, location))
def update_rc(basename, priorities, force=False): run('update-rc.d {} {} {}'.format('-f' if force else '', basename, priorities), pty=False, use_sudo=True)
def rm(location, recursive=False, force=True): force = '-f' if force else '' recursive = '-r' if recursive else '' run('rm %s %s %s' % (force, recursive, location))