def __getitem__(self, k): """Retrieve a piece of information. :param k: Name """ try: modname, funcname = k.rsplit('.', 1) except ValueError: raise ConfigurationError( 'Not a valid info entry (must be `module.name`): {}'.format(k)) funcname = 'info_{}'.format(funcname) modname = 'remand.lib.{}'.format(modname) # ensure module is imported try: mod = import_module(modname) except ImportError: raise ConfigurationError( 'Python module not found: {}'.format(modname)) func = getattr(mod, funcname, None) if func is None: raise ConfigurationError('Missing callable {} in {}'.format( funcname, modname)) return func()
def install_git(self, host, repo, user='******', branch='master', egg=None, upgrade=True, editable=False, protocol='git'): # FIXME: with newer git versions, we'd find a way here to pass in # the deployment key, which would allow not having to store # the key on the server if protocol in ('http', 'https'): url = 'git+{}://{}/{}@{}'.format(protocol, host, repo, branch) elif protocol == 'git': url = 'git+ssh://{}@{}/{}@{}'.format(user, host, repo, branch) else: raise ConfigurationError('Unknown protocol: {}'.format(protocol)) if egg is not None: url += '#egg=' + egg # FIXME: determine changes? self.install([url], upgrade=upgrade, editable=editable) return Changed(msg='Installed {}'.format(url))
def _by_short_name(cls, short_name): v = cls.registry.get(short_name, None) if v is None: raise ConfigurationError( 'Unknown {}: {!r}. Check your configuration setting.' .format(cls.__name__, short_name)) subclass = cls.registry[short_name] log.debug('{} {!r} -> {}'.format(cls.__name__, short_name, subclass.__name__)) return subclass
def verify_buffer(self, st, buf, remote_path): raise ConfigurationError( '{} does not verify buffers.'.format(self.__class__.__name__))
def verify_file(self, st, local_path, remote_path): raise ConfigurationError( '{} does not verify files.'.format(self.__class__.__name__))
def upload_file(local_path, remote_path=None, follow_symlink=True, create_parent=False): """Uploads a local file to a remote and if does not exist or differs from the local version, uploads it. To avoid having to transfer the file one or more times if unchanged, different methods for verification are available. These can be configured using the ``fs_remote_file_verify`` configuration variable. :param local_path: Local file to upload. If it is a symbolic link, it will be resolved first. :param remote_path: Remote name for the file. If ``None``, same as ``local_path``. If it points to a directory, the file will be uploaded to the directory. Symbolic links not pointing to a directory are an error. :param return: ``False`` if no upload was necessary, ``True`` otherwise. """ st, remote_path = _expand_remote_dest(local_path, remote_path) lst = os.stat(local_path) if follow_symlink else os.lstat(local_path) verifier = Verifier._by_short_name(config['fs_remote_file_verify'])() uploader = Uploader._by_short_name(config['fs_remote_file_upload'])() if lst is None: raise ConfigurationError( 'Local file {!r} does not exist'.format(local_path)) if S_ISLNK(lst.st_mode): # local file is a link rst = remote.lstat(remote_path) if rst: if not S_ISLNK(rst.st_mode): # remote file is not a link, unlink it remote.unlink(remote_path) elif remote.readlink(remote_path) != os.readlink(local_path): # non matching links remote.unlink(remote_path) else: # links pointing to the same target return Unchanged( msg='Symbolink link up-to-date: {}'.format(remote_path)) remote.symlink(os.readlink(local_path), remote_path) return Changed(msg='Created remote link: {}'.format(remote_path)) if not st or not verifier.verify_file(st, local_path, remote_path): if create_parent: create_dir(remote.path.dirname(remote_path)) uploader.upload_file(local_path, remote_path) if config.get_bool('fs_update_mtime'): times = (lst.st_mtime, lst.st_mtime) remote.utime(remote_path, times) log.debug('Updated atime/mtime: {}'.format(times)) return Changed(msg='Upload {} -> {}'.format(local_path, remote_path)) return Unchanged(msg='File up-to-date: {}'.format(remote_path))
def install_cert(cert, key, cert_name=None, key_name=None): """Installs an SSL certificate with including key on the remote Certificate filenames are unchanged, per default they will be installed in `/etc/ssl`, with the corresponding keys at `/etc/ssl/private`.""" cert_name = cert_name or os.path.basename(cert) key_name = key_name or os.path.basename(key) # small sanity check with open(cert) as f: if 'PRIVATE' in f.read(): raise ValueError( 'You seem to have passed a private key as a cert!') with open(key) as f: if 'PRIVATE' not in f.read(): raise ValueError( '{} does not seem to be a valid private key'.format(key)) # check if remote is reasonably secure cert_dir = config['sslcert_cert_dir'] cert_dir_st = remote.lstat(cert_dir) if not cert_dir_st: raise ConfigurationError( 'Remote SSL dir {} does not exist'.format(cert_dir)) key_dir = config['sslcert_key_dir'] key_dir_st = remote.lstat(key_dir) if not key_dir_st: raise ConfigurationError( 'Remote key dir {} does not exist'.format(key_dir)) SECURE_MODES = (0o700, 0o710) actual_mode = key_dir_st.st_mode & 0o777 if actual_mode not in SECURE_MODES: raise ConfigurationError( 'Mode of remote key dir {} is {:o}, should be one of {:o}'.format( key_dir, actual_mode, SECURE_MODES)) if key_dir_st.st_uid != 0: raise ConfigurationError( 'Remove key dir {} is not owned by root'.format(key_dir)) # we can safely upload the key and cert cert_rpath = remote.path.join(cert_dir, cert_name) key_rpath = remote.path.join(key_dir, key_name) changed = False changed |= fs.upload_file(cert, cert_rpath).changed changed |= fs.upload_file(key, key_rpath).changed changed |= fs.chmod(key_rpath, 0o640).changed changed |= fs.chown(key_rpath, uid='root', gid='ssl-cert').changed if changed: return Changed( msg='Uploaded key pair {}/{}'.format(cert_name, key_name)) return Unchanged( msg='Key pair {}/{} already uploaded'.format(cert_name, key_name))
def upload_buffer(self, buf, remote_path): raise ConfigurationError('{} does not support buffer uploads.'.format( self.__class__.__name__))
def upload_file(self, local_path, remote_path): raise ConfigurationError('{} does not support file uploads.'.format( self.__class__.__name__))