Esempio n. 1
0
class AuthorizedKeys(list):
    """
    Parser for OpenSSH authorized_keys file contents
    """
    def __init__(self, path=DEFAULT_AUTHORIZED_KEYS):
        self.log = Logger().default_stream
        self.path = path
        self.load()

    def load(self):
        """
        Load authorized keys file, discarding earlier data in the object.

        SSH v1 authorized_keys line: options, bits, exponent, modulus, comment)
        SSH v2 authorized_keys line: options, keytype, base64-encoded key, comment)'

        See man sshd for details.
        """
        self.__delslice__(0, len(self))

        if not os.path.isfile(self.path):
            self.log.debug('No such file: {0}'.format(self.path))
            return

        for line in [l.rstrip() for l in open(self.path, 'r').readlines()]:
            if line.startswith('#') or line.strip() == '':
                continue

            try:
                self.append(OpenSSHPublicKey(line))
            except SSHKeyError:
                pass
Esempio n. 2
0
    def __init__(self,
                 name=None,
                 description=None,
                 epilog=None,
                 debug_flag=True):
        self.name = os.path.basename(sys.argv[0])
        setproctitle('%s %s' % (self.name, ' '.join(sys.argv[1:])))
        signal.signal(signal.SIGINT, self.SIGINT)

        reload(sys)
        sys.setdefaultencoding('utf-8')

        if name is None:
            name = self.name

        # Set to True to avoid any messages from self.message to be printed
        self.silent = False

        self.logger = Logger(self.name)
        self.log = self.logger.default_stream

        self.subcommand_parser = None
        self.parser = argparse.ArgumentParser(
            prog=name,
            description=description,
            formatter_class=argparse.RawTextHelpFormatter,
            epilog=epilog,
            add_help=True,
            conflict_handler='resolve',
        )
        if debug_flag:
            self.parser.add_argument('--debug',
                                     action='store_true',
                                     help='Show debug messages')
Esempio n. 3
0
class KnownHosts(list):
    """
    Parser for OpenSSH known_hosts file contents
    """
    def __init__(self, path=DEFAULT_KNOWN_HOSTS, fingerprint_hash=None):
        self.log = Logger().default_stream
        self.path = path
        self.fingerprint_hash = fingerprint_hash
        self.load()

    def load(self):
        """
        Load known hosts file, discarding earlier data in the object.
        """
        del self[0:len(self)]

        if not os.path.isfile(self.path):
            self.log.debug('No such file: {}'.format(self.path))
            return

        for line in [l.rstrip() for l in open(self.path, 'r').readlines()]:
            if line.startswith('#') or line.strip() == '':
                continue

            # Strip list of hosts from line
            hosts, key = line.split(None, 1)
            hosts = hosts.split(',')

            try:
                key = KnownHostsEntry(key)
                if key not in self:
                    self.append(key)
                else:
                    key = self[self.index(key)]
                key.add_hosts(hosts)
            except SSHKeyError:
                pass

    def save(self, path=None):
        """Save known hosts

        Saves known hosts file, merging same keys to single line
        """
        if path is None:
            path = self.path
        try:
            with open(path, 'w') as fd:
                for entry in self:
                    fd.write('{}\n'.format(entry))
        except Exception as e:
            raise SSHKeyError('Error writing {}: {}'.format(path, e))

    def find_host_key(self, value):
        """Find key for hostname

        """
        for key in self:
            if value in key.hosts:
                return key
        return None
Esempio n. 4
0
class UserSSHKeys(dict):
    """
    List of user configured SSH keys to process
    """
    def __init__(self, authorized_keys=DEFAULT_AUTHORIZED_KEYS):
        self.log = Logger().default_stream
        self.__parse_user_keyfiles()
        self.authorized_keys = AuthorizedKeys(authorized_keys)

    def __parse_user_keyfiles(self):
        """
        Parses any normal user keyfiles in ~/.ssh directory automatically.

        These keys are marked by default not to be automatically loaded:
        to enable, add key path to ~/.ssh/sshkeys.conf
        """

        user_sshdir = os.path.expanduser('~/.ssh')
        if not os.path.isdir(user_sshdir):
            return

        paths = filter(lambda x: os.path.isfile(x), [
            os.path.join(user_sshdir, x) for x in filter(
                lambda x: x not in SSH_CONFIG_FILES and os.path.splitext(x)[1]
                != '.pub', os.listdir(user_sshdir))
        ])
        for path in paths:
            try:
                sshkey = SSHKeyFile(self, path)
            except SSHKeyError, emsg:
                self.log.debug(emsg)
                continue

            self[sshkey.path] = sshkey
Esempio n. 5
0
 def __init__(self, path=None):
     self.defaults = {}
     self.patterns = []
     self.log = Logger().default_stream
     self.path = path is not None and path or os.path.expanduser(
         '~/.ssh/config')
     self.reload()
Esempio n. 6
0
class AuthorizedKeys(list):
    """
    Parser for OpenSSH authorized_keys file contents
    """
    def __init__(self, path=DEFAULT_AUTHORIZED_KEYS):
        self.log = Logger().default_stream
        self.path = path
        self.load()

    def load(self):
        """
        Load authorized keys file, discarding earlier data in the object.

        SSH v1 authorized_keys line: options, bits, exponent, modulus, comment)
        SSH v2 authorized_keys line: options, keytype, base64-encoded key, comment)'

        See man sshd for details.
        """
        self.__delslice__(0, len(self))

        if not os.path.isfile(self.path):
            self.log.debug('No such file: {0}'.format(self.path))
            return

        for line in [l.rstrip() for l in open(self.path, 'r').readlines()]:
            if line.startswith('#') or line.strip() == '':
                continue

            try:
                self.append(OpenSSHPublicKey(line))
            except SSHKeyError:
                pass
Esempio n. 7
0
class KnownHosts(list):
    """
    Parser for OpenSSH known_hosts file contents
    """
    def __init__(self, path=DEFAULT_KNOWN_HOSTS, fingerprint_hash=None):
        self.log = Logger().default_stream
        self.path = path
        self.fingerprint_hash = fingerprint_hash
        self.load()

    def load(self):
        """
        Load known hosts file, discarding earlier data in the object.
        """
        del self[0:len(self)]

        if not os.path.isfile(self.path):
            self.log.debug('No such file: {}'.format(self.path))
            return

        for line in [l.rstrip() for l in open(self.path, 'r').readlines()]:
            if line.startswith('#') or line.strip() == '':
                continue

            # Strip list of hosts from line
            hosts, key = line.split(None, 1)
            hosts = hosts.split(',')

            try:
                key = KnownHostsEntry(key)
                if key not in self:
                    self.append(key)
                else:
                    key = self[self.index(key)]
                key.add_hosts(hosts)
            except SSHKeyError:
                pass

    def save(self, path=None):
        """Save known hosts

        Saves known hosts file, merging same keys to single line
        """
        if path is None:
            path = self.path
        try:
            with open(path, 'w') as fd:
                for entry in self:
                    fd.write('{}\n'.format(entry))
        except Exception as e:
            raise SSHKeyError('Error writing {}: {}'.format(path, e))

    def find_host_key(self, value):
        """Find key for hostname

        """
        for key in self:
            if value in key.hosts:
                return key
        return None
    def __init__(self, config, name, **kwargs):
        self.log = Logger().default_stream
        self.name = name
        self.description = name
        self.connect_command = DEFAULT_CONNECT_COMMAND
        self.command_separator = DEFAULT_COMMAND_SEPARATOR
        self.update_commands = []
        self.servers = []

        for k in ('description', 'command_separator'):
            if k in kwargs:
                setattr(self, k, kwargs[k])

        if 'connect' in kwargs:
            self.connect_command = kwargs['connect']

        if 'commands' in kwargs:
            self.update_commands = kwargs['commands']

        if 'servers' in kwargs:
            names = kwargs['servers']
            if not isinstance(names, list):
                names = [names]

            for name in names:
                self.servers.append(Server(self, name))

        self.modified = False
Esempio n. 9
0
    def __init__(self, oid):
        self.log = Logger('snmp').default_stream
        self.oid = self.__format_oid__(oid)
        self.oid_string = self.__format_oid_string__(oid)

        self._parent = None
        self._next = None
Esempio n. 10
0
    def __init__(self, path):
        self.operating_systems = []
        self.servers = []
        self.path = path
        self.log = Logger().default_stream

        if os.path.isfile(self.path):
            self.load()
Esempio n. 11
0
    def __init__(self, oid, reload_interval=60):
        Script.__init__(self)
        self.log = Logger('snmp').default_stream

        self.reload_interval = reload_interval
        self.last_reload = None

        self.add_argument('-g', '--get', help='SNMP GET request')
        self.add_argument('-n', '--next', help='SNMP GET request')
        self.add_argument('-t',
                          '--tree',
                          action='store_true',
                          help='Show OID tree')

        self.tree = Tree(oid)
Esempio n. 12
0
    def __init__(self, user_keys, path):
        self.log = Logger().default_stream
        self.user_keys = user_keys

        self.path = os.path.realpath(path)
        self.available = os.access(path, os.R_OK) and True or False

        self.autoload = False
        self.update({ 'bits': None, 'fingerprint': None, 'path': None, 'algorithm': None, })

        public_key = '%s.pub' % self.path
        if not os.path.isfile(public_key):
            self.available = False
            return

        cmd = ( 'ssh-keygen', '-l', '-f',  public_key )
        p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
        (stdout, stderr) = p.communicate()
        l = stdout.split('\n')[0].rstrip()

        if p.returncode != 0:
            raise SSHKeyError('ERROR parsing public key: %s' % public_key)

        m = RE_KEYINFO.match(l)
        if not m:
            raise SSHKeyError('Unsupported public key output: %s' % l)

        for k, v in m.groupdict().items():
            if k == 'path':
                k = 'public_key_path'
            self[k] = v
Esempio n. 13
0
    def __init__(self, name=None, description=None, epilog=None, debug_flag=True):
        self.name = os.path.basename(sys.argv[0])
        setproctitle('%s %s' % (self.name, ' '.join(sys.argv[1:])))
        signal.signal(signal.SIGINT, self.SIGINT)

        reload(sys)
        sys.setdefaultencoding('utf-8')

        if name is None:
            name = self.name

        # Set to True to avoid any messages from self.message to be printed
        self.silent = False

        self.logger = Logger(self.name)
        self.log = self.logger.default_stream

        self.subcommand_parser = None
        self.parser = argparse.ArgumentParser(
            prog=name,
            description=description,
            formatter_class=argparse.RawTextHelpFormatter,
            epilog=epilog,
            add_help=True,
            conflict_handler='resolve',
        )
        if debug_flag:
            self.parser.add_argument('--debug', action='store_true', help='Show debug messages')

        self.parser.add_argument('--insecure', action='store_false', help='No HTTPS certificate validation')
        self.parser.add_argument('-B', '--browser',
            choices=('chrome','chromium','firefox'),
            help='Browser for cookie stealing'
        )
Esempio n. 14
0
    def __init__(self, oid):
        self.log = Logger('snmp').default_stream
        self.oid = self.__format_oid__(oid)
        self.oid_string = self.__format_oid_string__(oid)

        self._parent = None
        self._next = None
Esempio n. 15
0
    def __init__(self, config, name, **kwargs):
        self.log = Logger().default_stream
        self.name = name
        self.description = name
        self.connect_command = DEFAULT_CONNECT_COMMAND
        self.command_separator = DEFAULT_COMMAND_SEPARATOR
        self.update_commands = []
        self.servers = []

        for k in ( 'description', 'command_separator', ):
            if k in kwargs:
                setattr(self, k, kwargs[k])

        if 'connect' in kwargs:
            self.connect_command = kwargs['connect']

        if 'commands' in kwargs:
            self.update_commands = kwargs['commands']

        if 'servers' in kwargs:
            names = kwargs['servers']
            if not isinstance(names, list):
                names = [names]

            for name in names:
                self.servers.append(Server(self, name))

        self.modified = False
Esempio n. 16
0
    def __init__(self, db_path, tables_sql=None, foreign_keys=True):
        """
        Opens given database reference. If tables_sql list is given,
        each SQL command in the list is executed to initialize the
        database.
        """
        self.log = Logger('sqlite').default_stream
        self.db_path = db_path

        if db_path is None:
            raise SQLiteError('Database path is None')

        db_dir = os.path.dirname(db_path)
        if db_dir and not os.path.isdir(db_dir):
            try:
                os.makedirs(db_dir)
            except IOError as e:
                raise SQLiteError('Error creating directory {0}: {1}'.format(
                    db_dir, e))

        self.conn = sqlite3.Connection(self.db_path)

        c = self.cursor
        if foreign_keys:
            c.execute('PRAGMA foreign_keys=ON')
            c.fetchone()

        if tables_sql:
            for q in tables_sql:
                try:
                    c.execute(q)
                except sqlite3.OperationalError as e:
                    raise SQLiteError('Error executing SQL:\n{0}\n{1}'.format(
                        q, e))
Esempio n. 17
0
    def __init__(self, value=None, input_format=None):
        self.log = Logger('dates').default_stream

        if value is None:
            self.value = datetime.now().date()

        elif isinstance(value, Day):
            self.value = value.value

        elif isinstance(value, date):
            self.value = value

        elif isinstance(value, datetime):
            self.value = value.date()

        elif isinstance(value, time.struct_time):
            self.value = date(*value[:3])

        elif isinstance(value, str) and input_format is None and value == '':
            self.value = datetime.now().date()

        else:
            input_format = input_format is not None and input_format or DEFAULT_DATE_FORMAT
            try:
                self.value = datetime.strptime(str(value), input_format).date()
            except ValueError:
                try:
                    self.value = date(*time.localtime(int(value))[:3])
                except ValueError:
                    raise DatesError('Error parsing date: {0}'.format(value))
Esempio n. 18
0
    def __init__(self,
                 value=None,
                 input_format=DEFAULT_DATE_FORMAT,
                 firstweekday=WEEK_START_DEFAULT,
                 workdays=None,
                 workdays_per_week=WORKDAYS_PER_WEEK):

        self.__next = 0
        self.log = Logger('dates').default_stream

        day = Day(value=value, input_format=input_format)

        if firstweekday in WEEKDAY_NAMES:
            self.firstweekday = WEEKDAY_NAMES.index(firstweekday)
        else:
            try:
                self.firstweekday = int(firstweekday)
                if self.firstweekday < 0 or self.firstweekday > 6:
                    raise ValueError
            except ValueError:
                raise ValueError(
                    'Invalid first week day index: {0}'.format(firstweekday))
        wday = (day.value.isoweekday() + (7 - self.firstweekday)) % 7

        self.first = day - wday
        self.last = self.first + 6
        self.weeknumber = int(self.first.value.strftime('%U'))

        self.workdays = []
        if workdays is not None:
            if not isinstance(workdays, iterable):
                raise ValueError(
                    'Invalid workdays index list parameter: {0}'.format(
                        workdays))
            for i in workdays:
                try:
                    i = int(i)
                    if i < 0 or i > 6:
                        raise ValueError
                    self.workdays.append(self.first + i)
                except ValueError:
                    raise ValueError(
                        'Invalid workdays index list parameter: {0}'.format(
                            workdays))

        else:
            try:
                workdays_per_week = int(workdays_per_week)
                if workdays_per_week < 0 or workdays_per_week > 7:
                    raise ValueError
            except ValueError:
                raise ValueError(
                    'Invalid value for workdays_per_week: {0}'.format(
                        workdays_per_week))
            self.workdays = [
                self[i]
                for i in filter(lambda i: i <= 6, range(0, workdays_per_week))
            ]

        self.workdays.sort()
Esempio n. 19
0
    def __init__(self, name=None, description=None, epilog=None, debug_flag=True):
        self.name = os.path.basename(sys.argv[0])
        signal.signal(signal.SIGINT, self.SIGINT)

        if has_setproctitle:
            setproctitle('{0} {1}'.format(self.name, ' '.join(sys.argv[1:])))

        if sys.version_info.major < 3:
            reload(sys)
            sys.setdefaultencoding('utf-8')

        if name is None:
            name = self.name

        # Set to True to avoid any messages from self.message to be output
        self.silent = False

        self.logger = Logger(self.name)
        self.log = self.logger.default_stream

        self.parser = argparse.ArgumentParser(
            prog=name,
            description=description,
            formatter_class=argparse.RawTextHelpFormatter,
            epilog=epilog,
            add_help=True,
            conflict_handler='resolve',
        )

        if debug_flag:
            self.parser.add_argument('--debug', action='store_true', help='Show debug messages')

        self.subcommand_parser = None
Esempio n. 20
0
 def __init__(self, name):
     super(threading.Thread, self).__init__()
     self.log = Logger(name).default_stream
     self.status = 'not running'
     self.setDaemon(True)
     self.setName(name)
     self._stop_event = threading.Event()
Esempio n. 21
0
    def __init__(self,
                 src,
                 dst,
                 flags=DEFAULT_RSYNC_FLAGS,
                 output_format=DEFAULT_OUTPUT_FORMAT):
        self.log = Logger('rsync').default_stream
        self.src = src
        self.dst = dst
        self.flags = flags
        self.output_format = output_format

        cmd = CommandPathCache().which('rsync')
        if cmd is None:
            raise RsyncError('No such command: rsync')
        self.command = [cmd] + flags + [
            self.output_format, '{0}'.format(src), '{0}'.format(dst)
        ]
Esempio n. 22
0
    def __init__(self, path):
        self.operating_systems = []
        self.servers = []
        self.path = path
        self.log = Logger().default_stream

        if os.path.isfile(self.path):
            self.load()
Esempio n. 23
0
    def __init__(self, runner, show_colors=False):
        self.log = Logger().default_stream
        self.runner = runner
        self.show_colors = show_colors

        self.results = {
            'contacted': self.resultset_loader(self, 'contacted'),
            'dark': self.resultset_loader(self, 'dark'),
        }
Esempio n. 24
0
    def __init__(self,flags=[]):
        self.log = Logger('filesystems').default_stream

        if isinstance(flags, list):
            for k in flags: 
                self.set(k)
        
        if isinstance(flags, dict):
            for k, v in flags.items():
                self.set(k, v)
Esempio n. 25
0
    def __init__(self,src,dst,flags=DEFAULT_RSYNC_FLAGS,output_format=DEFAULT_OUTPUT_FORMAT):
        self.log = Logger('rsync').default_stream
        self.src = src
        self.dst = dst
        self.flags = flags
        self.output_format = output_format

        cmd = CommandPathCache().which('rsync')
        if cmd is None:
            raise RsyncError('No such command: rsync')
        self.command = [cmd] + flags + [ self.output_format, '{0}'.format(src),'{0}'.format(dst) ]
Esempio n. 26
0
class SSHConfig(dict):
    def __init__(self, path=None):
        self.defaults = {}
        self.patterns = []
        self.log = Logger().default_stream
        self.path = path is not None and path or os.path.expanduser(
            '~/.ssh/config')
        self.reload()

    def reload(self):
        self.clear()
        if not os.path.isfile(self.path):
            self.log.debug('No such file: {}'.format(self.path))
            return

        with open(self.path, 'r') as fd:
            host = None
            for line in [x.strip() for x in fd.readlines()]:
                if line == '' or line.startswith('#'):
                    continue

                if line[:5] == 'Host ':
                    host = SSHConfigHostPattern(self, line[5:])
                    for pattern in host.patterns:
                        self[pattern] = host
                    self.patterns.append(host)

                else:
                    host.parse(line)

        if '*' in self.keys():
            self.defaults.update(self.pop('*').items())

    def keys(self):
        return [k for k in sorted(super(SSHConfig, self).keys())]

    def items(self):
        return [(k, self[k]) for k in self.keys()]

    def values(self):
        return [self[k] for k in self.keys()]
Esempio n. 27
0
    def __init__(self, oid, reload_interval=60):
        Script.__init__(self)
        self.log = Logger('snmp').default_stream

        self.reload_interval = reload_interval
        self.last_reload = None

        self.add_argument('-g', '--get', help='SNMP GET request')
        self.add_argument('-n', '--next', help='SNMP GET request')
        self.add_argument('-t', '--tree', action='store_true', help='Show OID tree')

        self.tree = Tree(oid)
Esempio n. 28
0
class SSHConfig(dict):
    def __init__(self, path=None):
        self.defaults = {}
        self.patterns = []
        self.log = Logger().default_stream
        self.path = path is not None and path or os.path.expanduser('~/.ssh/config')
        self.reload()

    def reload(self):
        self.clear()
        if not os.path.isfile(self.path):
            self.log.debug('No such file: {}'.format(self.path))
            return

        with open(self.path, 'r') as fd:
            host = None
            for line in [x.strip() for x in fd.readlines()]:
                if line == '' or line.startswith('#'):
                    continue

                if line[:5] == 'Host ':
                    host = SSHConfigHostPattern(self, line[5:])
                    for pattern in host.patterns:
                        self[pattern] = host
                    self.patterns.append(host)

                else:
                    host.parse(line)

        if '*' in self.keys():
            self.defaults.update(self.pop('*').items())

    def keys(self):
        return [k for k in sorted(super(SSHConfig, self).keys())]

    def items(self):
        return [(k, self[k]) for k in self.keys()]

    def values(self):
        return [self[k] for k in self.keys()]
Esempio n. 29
0
 def __init__(self,
              mountpoints,
              device,
              mountpoint,
              filesystem,
              flags=None):
     self.log = Logger('filesystems').default_stream
     self.mountpoints = mountpoints
     self.device = device
     self.mountpoint = mountpoint
     self.filesystem = filesystem
     self.flags = FileSystemFlags(flags=flags if flags is not None else {})
     self.usage = {}
Esempio n. 30
0
class SSHConfig(dict):
    def __init__(self, path=None):
        self.defaults = {}
        self.log = Logger().default_stream
        self.path = path is not None and path or os.path.expanduser('~/.ssh/config')
        self.reload()

    def reload(self):
        self.clear()
        if not os.path.isfile(self.path):
            self.log.debug('No such file: %s' % self.path)
            return

        with open(self.path, 'r') as fd:
            host = None
            for l in [x.strip() for x in fd.readlines()]:
                if l=='' or l.startswith('#'):
                    continue

                if l[:5]=='Host ':
                    host = SSHConfigHost(self, l[5:])
                    self[host.name] = host

                else:
                    host.parse(l)

        if '*' in self.keys():
            self.defaults.update(self.pop('*').items())

    def keys(self):
        return [k for k in sorted(dict.keys(self))]

    def items(self):
        return [(k, self[k]) for k in self.keys()]

    def values(self):
        return [self[k] for k in self.keys()]
Esempio n. 31
0
    def __init__(self,value=None, input_format=DEFAULT_DATE_FORMAT,
                firstweekday=WEEK_START_DEFAULT):
        self.__next = 0
        self.log = Logger('dates').default_stream

        self.first = Day(Day(value=value, input_format=input_format).value.replace(day=1))
        self.days = calendar.monthrange(self.first.value.year,self.first.value.month)[1]
        self.last  = self.first+(self.days-1)

        self.firstweekday = firstweekday
        self.weeks = []
        week = Week(self.first, None, firstweekday=self.firstweekday)
        while week.first <= self.last:
            self.weeks.append(week)
            week+=1
Esempio n. 32
0
class UserSSHKeys(dict):
    """
    List of user configured SSH keys to process
    """
    def __init__(self, authorized_keys=DEFAULT_AUTHORIZED_KEYS):
        self.log = Logger().default_stream
        self.__parse_user_keyfiles()
        self.authorized_keys = AuthorizedKeys(authorized_keys)

    def __parse_user_keyfiles(self):
        """
        Parses any normal user keyfiles in ~/.ssh directory automatically.

        These keys are marked by default not to be automatically loaded:
        to enable, add key path to ~/.ssh/sshkeys.conf
        """

        user_sshdir = os.path.expanduser('~/.ssh')
        if not os.path.isdir(user_sshdir):
            return

        paths = filter(lambda x:
            os.path.isfile(x),
            [os.path.join(user_sshdir, x) for x in filter(lambda x:
                x not in SSH_CONFIG_FILES and os.path.splitext(x)[1]!='.pub',
                os.listdir(user_sshdir)
            )]
        )
        for path in paths:
            try:
                sshkey = SSHKeyFile(self, path)
            except SSHKeyError, emsg:
                self.log.debug(emsg)
                continue

            self[sshkey.path] = sshkey
Esempio n. 33
0
class RsyncCommand(object):
    """
    Wrapper to execute rsync to target nicely from python
    """
    def __init__(self,
                 src,
                 dst,
                 flags=DEFAULT_RSYNC_FLAGS,
                 output_format=DEFAULT_OUTPUT_FORMAT):

        self.log = Logger('rsync').default_stream
        self.src = src
        self.dst = dst
        self.flags = flags
        self.output_format = output_format

        cmd = CommandPathCache().which('rsync')
        if cmd is None:
            raise RsyncError('No such command: rsync')
        self.command = [cmd] + flags + [
            self.output_format, '{0}'.format(src), '{0}'.format(dst)
        ]

    def __str__(self):
        return ' '.join(self.command)

    def run(self, verbose=False):
        """
        Run the rsync command specific in __init__()
        """
        if not verbose and self.log.level == logging.DEBUG:
            verbose = True
        try:
            p = Popen(self.command, stdin=PIPE, stdout=PIPE, stderr=PIPE)
            self.log.debug('Running: {0}'.format(self))

            rval = None
            while rval is None:
                if verbose:
                    while True:
                        line = p.stdout.readline()
                        if line == '':
                            break
                        sys.stdout.write('{0}\n'.format(line.rstrip()))
                time.sleep(0.2)
                rval = p.poll()

            self.log.debug('Return code: {0}'.format(rval))
            if rval != 0:
                raise RsyncError('Error running command {0}: {1}'.format(
                    self, p.stderr.read()))

        except KeyboardInterrupt:
            self.log.debug('Rsync interrupted')
            raise KeyboardInterrupt
Esempio n. 34
0
    def __init__(self, db_path, tables_sql=None, foreign_keys=True):
        """
        Opens given database reference. If tables_sql list is given,
        each SQL command in the list is executed to initialize the
        database.
        """
        self.log = Logger('sqlite').default_stream
        self.db_path = db_path

        if db_path is None:
            raise SQLiteError('Database path is None')
        db_dir = os.path.dirname(db_path)
        if not os.path.isdir(db_dir):
            try:
                os.makedirs(db_dir)
            except IOError, (ecode, emsg):
                raise SQLiteError('Error creating directory %s: %s' %
                                  (db_dir, emsg))
Esempio n. 35
0
class RsyncCommand(object):
    """
    Wrapper to execute rsync to target nicely from python
    """
    def __init__(self, src, dst,
                 flags=DEFAULT_RSYNC_FLAGS,
                 output_format=DEFAULT_OUTPUT_FORMAT):

        self.log = Logger('rsync').default_stream
        self.src = src
        self.dst = dst
        self.flags = flags
        self.output_format = output_format

        cmd = CommandPathCache().which('rsync')
        if cmd is None:
            raise RsyncError('No such command: rsync')
        self.command = [cmd] + flags + [self.output_format, '{0}'.format(src), '{0}'.format(dst)]

    def __str__(self):
        return ' '.join(self.command)

    def run(self, verbose=False):
        """
        Run the rsync command specific in __init__()
        """
        if not verbose and self.log.level == logging.DEBUG:
            verbose = True
        try:
            p = Popen(self.command, stdin=PIPE, stdout=PIPE, stderr=PIPE)
            self.log.debug('Running: {0}'.format(self))

            rval = None
            while rval is None:
                if verbose:
                    while True:
                        line = p.stdout.readline()
                        if line == '':
                            break
                        sys.stdout.write('{0}\n'.format(line.rstrip()))
                time.sleep(0.2)
                rval = p.poll()

            self.log.debug('Return code: {0}'.format(rval))
            if rval != 0:
                raise RsyncError('Error running command {0}: {1}'.format(self, p.stderr.read()))

        except KeyboardInterrupt:
            self.log.debug('Rsync interrupted')
            raise KeyboardInterrupt
Esempio n. 36
0
    def __init__(self, user_keys, path):
        self.log = Logger().default_stream
        self.user_keys = user_keys

        self.path = os.path.realpath(path)
        self.available = os.access(path, os.R_OK) and True or False

        self.autoload = False
        self.update({
            'bits': None,
            'fingerprint': None,
            'path': None,
            'algorithm': None
        })

        public_key = '{}.pub'.format(self.path)
        if not os.path.isfile(public_key):
            self.available = False
            return

        cmd = ('ssh-keygen', '-l', '-f', public_key)
        p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
        (stdout, stderr) = [str(x, 'utf-8') for x in p.communicate()]
        line = stdout.split('\n')[0].rstrip()

        if p.returncode != 0:
            raise SSHKeyError(
                'ERROR parsing public key: {}'.format(public_key))

        data = parse_public_key_line_pattern(line)
        if not data:
            raise SSHKeyError('Unsupported public key output: {}'.format(line))

        for k, v in data.items():
            if k == 'path':
                k = 'public_key_path'
            self[k] = v
Esempio n. 37
0
class Script(object):
    """
    Class for common CLI tool script
    """
    def __init__(self, name=None, description=None, epilog=None, debug_flag=True):
        self.name = os.path.basename(sys.argv[0])
        signal.signal(signal.SIGINT, self.SIGINT)

        if has_setproctitle:
            setproctitle('{0} {1}'.format(self.name, ' '.join(sys.argv[1:])))

        if sys.version_info.major < 3:
            reload(sys)  # noqa
            sys.setdefaultencoding('utf-8')

        if name is None:
            name = self.name

        # Set to True to avoid any messages from self.message to be output
        self.silent = False

        self.logger = Logger(self.name)
        self.log = self.logger.default_stream

        self.parser = argparse.ArgumentParser(
            prog=name,
            description=description,
            formatter_class=argparse.RawTextHelpFormatter,
            epilog=epilog,
            add_help=True,
            conflict_handler='resolve',
        )

        if debug_flag:
            self.parser.add_argument('--debug', action='store_true', help='Show debug messages')

        self.subcommand_parser = None

    def SIGINT(self, signum, frame):
        """
        Parse SIGINT signal by quitting the program cleanly with exit code 1
        """
        for t in [t for t in threading.enumerate() if t.name != 'MainThread']:
            if hasattr(t, 'stop') and callable(t.stop):
                t.stop()

        for t in [t for t in threading.enumerate() if t.name != 'MainThread']:
            t.join()

        self.exit(1)

    def wait(self, poll_interval=1):
        """
        Wait for running threads to finish.
        Poll interval is time to wait between checks for threads
        """
        while True:
            active = [t for t in threading.enumerate() if t.name != 'MainThread']
            if not len(active):
                break
            self.log.debug('Waiting for {0:d} threads'.format(len(active)))
            time.sleep(poll_interval)

    def exit(self, value=0, message=None):
        """
        Exit the script with given exit value.
        If message is not None, it is output to stdout
        """
        if isinstance(value, bool):
            if value:
                value = 0
            else:
                value = 1
        else:
            try:
                value = int(value)
                if value < 0 or value > 255:
                    raise ValueError
            except ValueError:
                value = 1

        if message is not None:
            self.message(message)

        for t in [t for t in threading.enumerate() if t.name != 'MainThread']:
            if hasattr(t, 'stop') and callable(t.stop):
                t.stop()

        while True:
            active = [t for t in threading.enumerate() if t.name != 'MainThread']
            if not len(active):
                break
            time.sleep(0.1)

        sys.exit(value)

    def message(self, message):
        if self.silent:
            return
        sys.stdout.write('{0}\n'.format(message))

    def error(self, message):
        sys.stderr.write('{0}\n'.format(message))

    def add_subcommand(self, command):
        """Add a subcommand parser instance

        Register named subcommand parser to argument parser

        Subcommand parser must be an instance of ScriptCommand class.

        Example usage:

        class ListCommand(ScriptCommand):
            def run(self, args):
                self.message('Listing stuff')

        parser.add_subcommand(ListCommand('list', 'List stuff from script'))

        """

        if self.subcommand_parser is None:
            self.subcommand_parser = self.parser.add_subparsers(
                dest='command', help='Please select one command mode below',
                title='Command modes'
            )
            self.subcommands = {}

        if not isinstance(command, ScriptCommand):
            raise ScriptError('Subcommand must be a ScriptCommand instance')

        parser = self.subcommand_parser.add_parser(
            command.name,
            help=command.short_description,
            description=command.description,
            epilog=command.epilog,
            formatter_class=argparse.RawTextHelpFormatter,
        )
        self.subcommands[command.name] = command
        command.script = self

        if callable(getattr(command, '__register_arguments__', None)):
            command.__register_arguments__(parser)

        return parser

    def usage_error(self, *args, **kwargs):
        return self.parser.error(*args, **kwargs)

    def add_argument(self, *args, **kwargs):
        """
        Shortcut to add argument to main argumentparser instance
        """
        self.parser.add_argument(*args, **kwargs)

    def __process_args__(self, args):
        """Process args
        Process args from parse_*args CalledProcessError
        """
        if getattr(args, 'debug', None):
            self.logger.set_level('DEBUG')

        elif getattr(args, 'quiet', None):
            self.silent = True

        elif getattr(args, 'verbose', None):
            self.logger.set_level('INFO')

        if self.subcommand_parser is not None and args.command is not None:
            if hasattr(self.subcommands[args.command], 'parse_args'):
                args = self.subcommands[args.command].parse_args(args)
            self.subcommands[args.command].run(args)

        return args

    def parse_args(self):
        """
        Call parse_args for parser and check for default logging flags
        """
        return self.__process_args__(self.parser.parse_args())

    def parse_known_args(self):
        """
        Call parse_args for parser and check for default logging flags
        """
        args, other_args = self.parser.parse_known_args()
        args = self.__process_args__(args)
        return args, other_args

    def execute(self, args, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr, dryrun=False):
        """
        Default wrapper to execute given interactive shell command
        with standard stdin, stdout and stderr
        """
        if isinstance(args, str):
            args = args.split()

        if not isinstance(args, list):
            raise ValueError('Execute arguments must be a list')

        if dryrun:
            self.log.debug('would execute: {0}'.format(' '.join(args)))
            return 0

        p = Popen(args, stdin=stdin, stdout=stdout, stderr=stderr)
        p.wait()
        return p.returncode

    def check_output(self, args):
        """
        Wrapper for subprocess.check_output to be executed in script context
        """
        if isinstance(args, str):
            args = [args]
        try:
            return check_output(args)

        except IOError as e:
            raise ScriptError(e)

        except OSError as e:
            raise ScriptError(e)

        except CalledProcessError as e:
            raise ScriptError(e)
Esempio n. 38
0
class Server(object):
    """
    Configuration for one server
    """
    def __init__(self, osgroup, name, description=None):
        self.log = Logger().default_stream
        self.osgroup = osgroup
        self.name = name
        self.description = description

    def __repr__(self):
        if self.description is not None:
            return '{0} ({1})'.format(self.name, self.description)
        else:
            return '{0}'.format(self.name)

    def __eq__(self, other):
        if isinstance(other, str):
            return self.name == other
        return self.name == other.name

    def __ne__(self, other):
        if isinstance(other, str):
            return self.name != other
        return self.name != other.name

    def __gt__(self, other):
        if isinstance(other, str):
            return self.name > other
        return self.name > other.name

    def __lt__(self, other):
        if isinstance(other, str):
            return self.name < other
        return self.name < other.name

    def __ge__(self, other):
        if isinstance(other, str):
            return self.name >= other
        return self.name >= other.name

    def __le__(self, other):
        if isinstance(other, str):
            return self.name <= other
        return self.name <= other.name

    @property
    def connect_command(self):
        return [
            x == 'SERVER' and self.name or x
            for x in self.osgroup.connect_command
        ]

    def check_output(self, command):
        """
        Wrapper to execute and check output of a command without
        interactive console actions

        Splits string automatically to words so pass a list if you
        need to escape stuff properly.
        """
        if not isinstance(command, list):
            command = [command]

        cmd = self.connect_command + command
        p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
        (stdout, stderr) = p.communicate()
        return p.returncode, stdout.rstrip('\n'), stderr.rstrip('\n')

    def shell(self, command):
        """
        Wrapper to run interactive shell command on the host, using
        current terminal for stdin, stdout and stderr

        Splits string automatically to words so pass a list if you
        need to escape stuff properly.

        Returns command return code
        """
        if not isinstance(command, list):
            command = [command]

        cmd = self.connect_command + command
        p = Popen(cmd, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr)
        p.wait()
        return p.returncode

    def update(self):
        """
        Wrapper to call correct update commands for this host
        """
        if not self.osgroup.update_commands:
            self.log.debug('No update commands for OS {0}'.format(
                self.osgroup.name))
            return

        self.log.debug("Running: {0} '{1}'".format(
            ' '.join(self.connect_command),
            self.osgroup.command_separator.join(self.osgroup.update_commands)))

        cmd = self.connect_command + [
            self.osgroup.command_separator.join(self.osgroup.update_commands)
        ]
        p = Popen(cmd, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr)
        p.wait()
        return p.returncode

    def remove(self):
        """Remove from configuration

        Remove this server from configuration

        """
        return self.osgroup.remove_server(self)
Esempio n. 39
0
class ServerConfigFile(object):
    """
    Parser for configuration file describing servers and their
    operating systems.
    """
    def __init__(self, path):
        self.operating_systems = []
        self.servers = []
        self.path = path
        self.log = Logger().default_stream

        if os.path.isfile(self.path):
            self.load()

    @property
    def osnames(self):
        return [x.name for x in self.operating_systems]

    def load(self):
        self.operating_systems = []
        self.servers = []

        try:
            config = ConfigObj(self.path)
        except ValueError as e:
            raise ValueError('Error parsing {0}: {1}'.format(self.path, e))

        osgroup = None
        for key, section in config.items():
            if 'commands' in section:
                if key in self.servers:
                    raise ValueError(
                        'Duplicate OS group name: {0}'.format(key))
                osgroup = OperatingSystemGroup(self, key, **section)
                self.operating_systems.append(osgroup)
                self.servers.extend(osgroup.servers)
            else:
                if osgroup is None:
                    raise ValueError(
                        'Server in configuration file but no OS group defined yet'
                    )
                self.servers.append(Server(osgroup, section))

        return

    def save(self):
        config = ConfigObj()

        for osgroup in self.operating_systems:
            section = {
                'commands': osgroup.update_commands,
            }

            if osgroup.description is not None and osgroup.description != '':
                section['description'] = osgroup.description

            if osgroup.servers:
                section['servers'] = osgroup.servers

            if osgroup.connect_command != DEFAULT_CONNECT_COMMAND:
                section['connect'] = osgroup.connect_command

            if osgroup.command_separator != DEFAULT_COMMAND_SEPARATOR:
                section['command_separator'] = osgroup.command_separator

            config[osgroup.name] = section
            for server in osgroup.servers:
                if server.description:
                    config[server.name] = {'description': server.description}

        self.log.debug('Saving configuration to {0}'.format(self.path))
        config.write(outfile=open(self.path, 'w'))

    def match_os(self, name):
        for operating_system in self.operating_systems:
            if operating_system.name == name:
                return operating_system

        raise ValueError('Unknown OS: {0}'.format(name))
Esempio n. 40
0
class OperatingSystemGroup(object):
    """
    Group of operating systems in configuration file
    """
    def __init__(self, config, name, **kwargs):
        self.log = Logger().default_stream
        self.name = name
        self.description = name
        self.connect_command = DEFAULT_CONNECT_COMMAND
        self.command_separator = DEFAULT_COMMAND_SEPARATOR
        self.update_commands = []
        self.servers = []

        for k in ('description', 'command_separator'):
            if k in kwargs:
                setattr(self, k, kwargs[k])

        if 'connect' in kwargs:
            self.connect_command = kwargs['connect']

        if 'commands' in kwargs:
            self.update_commands = kwargs['commands']

        if 'servers' in kwargs:
            names = kwargs['servers']
            if not isinstance(names, list):
                names = [names]

            for name in names:
                self.servers.append(Server(self, name))

        self.modified = False

    def __repr__(self):
        return '{0}: {1} ({2:d} servers)'.format(self.name, self.description,
                                                 len(self.servers))

    @property
    def command_separator(self):
        return self._command_separator

    @command_separator.setter
    def command_separator(self, value):
        self._command_separator = value
        self.modified = True

    @property
    def connect_command(self):
        return self._connect_command

    @connect_command.setter
    def connect_command(self, value):
        if isinstance(value, str):
            value = value.split()
        self._connect_command = value
        self.modified = True

    @property
    def update_command(self):
        return self._update_commands

    @update_command.setter
    def update_commands(self, value):
        if isinstance(value, str):
            value = [value]
        self._update_commands = value
        self.modified = True

    @property
    def descripion(self):
        return self._descripion

    @descripion.setter
    def descripion(self, value):
        self._descripion = value
        self.modified = True

    def add_server(self, name):
        if name in [s.name for s in self.servers]:
            self.log.debug(
                'Error adding: server already in group: {0}'.format(name))
            return

        self.servers.append(Server(self, name))
        self.modified = True

    def remove_server(self, name):
        try:
            server = [s for s in self.servers if s.name == name][0]
            self.servers.remove(server)
            self.modified = True
        except IndexError:
            self.log.debug(
                'Error removing: server not in group: {0}'.format(name))
            return
Esempio n. 41
0
class AuthorizedKeys(dict):
    """
    Parser for OpenSSH authorized_keys file contents
    """
    def __init__(self, path=DEFAULT_AUTHORIZED_KEYS):
        self.log = Logger().default_stream
        self.path = path
        self.load()

    def load(self):
        """
        Load authorized keys file, discarding earlier data in the object.

        SSH v1 authorized_keys line: options, bits, exponent, modulus, comment)
        SSH v2 authorized_keys line: options, keytype, base64-encoded key, comment)'

        See man sshd for details.
        """
        self.clear()
        if not os.path.isfile(self.path):
            self.log.debug('No such file: {0}'.format(self.path))
            return

        for l in [l.rstrip() for l in open(self.path, 'r').readlines()]:
            if l.startswith('#') or l.strip() == '':
                continue

            entry = {}
            parts = l.split()

            if parts[0][0] in string.digits and len(parts) == 4:
                entry['keyformat'] = 1
                entry['bits'] = int(parts[0])
                entry['exponent'] = parts[1]
                entry['modulus'] = parts[2]
                entry['comment'] = parts[3]
                entry['options'] = None

            elif parts[0] in ( 'ssh-dsa', 'ssh-rsa', ):
                entry['keyformat'] = 2
                entry['keytype'] = parts[0]
                entry['key_base64'] = parts[1]
                entry['comment'] = len(parts) > 2 and parts[2] or ''
                entry['options'] = None

            else:
                entry['options'] = parts[0]
                parts = parts[1:]

                if not parts:
                    continue

                if parts[0] in ( 'ssh-dsa', 'ssh-rsa', ) and len(parts) ==  3:
                    entry['keyformat'] = 2
                    entry['keytype'] = parts[0]
                    entry['key_base64'] = parts[1]
                    entry['comment'] = parts[2]

                elif parts[0] in string.digits:
                    entry['keyformat'] = 1
                    entry['keyformat'] = 1
                    entry['bits'] = int(parts[0])
                    entry['exponent'] = parts[1]
                    entry['modulus'] = parts[2]
                    entry['comment'] = parts[3]
Esempio n. 42
0
 def __init__(self, path=DEFAULT_AUTHORIZED_KEYS, fingerprint_hash=None):
     self.log = Logger().default_stream
     self.path = path
     self.fingerprint_hash = fingerprint_hash
     self.load()
Esempio n. 43
0
File: agent.py Progetto: hile/seine
class SNMPAgent(Script):
    """SNMP Agent implementation

    SNMP agent daemon for net-snmpd pass_persist mode

    Example usage:

        agent = SNMPAgent('.1.2.3.4')
        tree = agent.register_tree('1.2.3.4.1')
        tree.register('1.2.3.4.1.1', 'integer', 1)
        tree.register('1.2.3.4.1.2', 'integer', 2)

        agent.register_tree('1.2.3.4.2')
        agent.register('1.2.3.4.2.1', 'integer', 1)
        agent.register('1.2.3.4.2.4', 'integer', 4)
        agent.register('1.2.3.4.2.3', 'integer', 3)
        agent.register('1.2.3.4.2.2', 'integer', 2)

        tree = agent.register_tree('1.2.3.4.3')
        tree.register('1.2.3.4.3.1.1', 'integer', 1)
        tree.add_values('string', [x for x in string.digits[2:]])

        tree = agent.register_tree('1.2.3.4.4')
        tree.add_prefix_map([
            { 'oid': '1.2.3.4.4.1', 'key': 'string', 'values': [x for x in string.letters[:10]]},
            { 'oid': '1.2.3.4.4.2', 'key': 'integer', 'values': [x for x in string.digits[:10]]},
        ])

        agent.run()
    """
    def __init__(self, oid, reload_interval=60):
        super(SNMPAgent, self).__init__()
        self.log = Logger('snmp').default_stream

        self.reload_interval = reload_interval
        self.last_reload = None
        self.args = None

        self.add_argument('-g', '--get', help='SNMP GET request')
        self.add_argument('-n', '--next', help='SNMP GET request')
        self.add_argument('-t', '--tree', action='store_true', help='Show OID tree')

        self.tree = Tree(oid)

    def __SIGHUP__(self, signum, frame):
        """
        Signal handler to reload configuration. Note this requires also the
        IOError processing in main input loop below
        """
        self.log.debug('Reloading from signal')
        self.reload()

    def parse_args(self):
        if self.args is None:
            self.args = super(SNMPAgent, self).parse_args()
        return self.args

    def register_tree(self, oid):
        return self.tree.add(Tree(oid))

    def register(self, oid, key, value):
        return self.tree.add(Item(oid, key, value))

    def GET(self, oid):
        """GET from agent

        Return given OID from agent tree or None

        """
        try:
            return self.tree.GET(oid)
        except SNMPError as e:
            self.log.debug(e)
            return None

    def NEXT(self, oid):
        """NEXT from agent

        Return given NEXT from agent tree or None

        """
        try:
            return self.tree.NEXT(oid)
        except SNMPError as e:
            self.log.debug(e)
            return None

    def SET(self, oid, value):
        """SET registered OID

        Set value for registered OID in agent tree.

        Note: By default items registered are readonly.


        """
        return self.tree.SET(oid, value)

    def clear(self):
        return self.tree.clear()

    def reload(self):
        """
        This method must be implemented in a child class. It is used to
        reload the SNMP tree data from files, if possible.
        """
        raise NotImplementedError('You must implement reload in child class')

    def run(self):
        """SNMP Agent main loop

        Main loop to execute for agent. You can either run this in:

        - 'pass' mode: get/next for single OID by passing in OptionParser
          'options' value 'get' or 'next' (snmpd.conf 'pass' agent)
        - 'pass_persist' mode: without any options, in which case the loop
           acts as permanent snmpd pass_persist agent.
        """

        self.args = self.parse_args()

        signal.signal(signal.SIGHUP, self.__SIGHUP__)

        if self.args.tree:
            self.message(self.tree.oid_string)

        elif self.args.get:
            entry = self.GET(self.args.get)
            if entry is not None:
                sys.stdout.write('{0}\n'.format(entry.get_response))

        elif self.args.next:
            entry = self.NEXT(self.args.next)
            if entry is not None:
                sys.stdout.write('{0}\n'.format(entry.next_response))

        if self.args.tree or self.args.get or self.args.next:
            self.exit(0)

        EOF = ''
        self.log.debug('Starting SNMP agent for OID {0}'.format(self.tree.oid))

        while True:
            ready = select.select([sys.stdin], [], [], 0.2)[0]
            if not ready:
                if self.last_reload is not None:
                    since_reload = time.time() - self.last_reload
                    if since_reload > self.reload_interval:
                        self.reload()
                        self.last_reload = time.time()
                else:
                    self.last_reload = time.time()
                continue

            try:
                cmd = sys.stdin.readline()
                if cmd == EOF:
                    break
                if cmd.strip() == '':
                    self.log.debug('Received agent shutdown')
                    break

                cmd = cmd.rstrip().lower()

                if cmd == 'quit':
                    return

                if cmd == 'reload':
                    self.reload()

                if cmd == 'ping':
                    sys.stdout.write('PONG\n')

                if cmd in ('set', 'get', 'getnext'):
                    oid = sys.stdin.readline()
                    if oid == EOF:
                        break
                    oid = oid.rstrip()

                if cmd == 'set':
                    value = sys.stdin.readline()
                    if value == EOF:
                        break
                    result = self.SET(oid, value)
                    sys.stdout.write('{0}\n'.format(result))

                elif cmd == 'get':
                    entry = self.GET(oid)
                    if entry is not None:
                        sys.stdout.write('{0}\n'.format(entry.get_response))
                    else:
                        sys.stdout.write('NONE\n')

                elif cmd == 'getnext':
                    entry = self.NEXT(oid)
                    if entry is not None:
                        sys.stdout.write('{0}\n'.format(entry.next_response))
                    else:
                        sys.stdout.write('NONE\n')

                else:
                    self.log.debug('Unknown command (should be get/set/getnext)')
                sys.stdout.flush()

            except IOError as e:
                # we get EINTR with SIGHUP and we can ignore it
                if e[0]==errno.EINTR:
                    continue
                self.log.debug('IOError: {0}'.format(e))
                return

            except KeyboardInterrupt:
                # Interactive mode, user interrupted
                self.log.debug('Quitting...')
                return
Esempio n. 44
0
class ServerConfigFile(object):
    """
    Parser for configuration file describing servers and their
    operating systems.
    """

    def __init__(self, path):
        self.operating_systems = []
        self.servers = []
        self.path = path
        self.log = Logger().default_stream

        if os.path.isfile(self.path):
            self.load()

    @property
    def osnames(self):
        return [x.name for x in self.operating_systems]

    def load(self):
        self.operating_systems = []
        self.servers = []

        try:
            config = ConfigObj(self.path)
        except ValueError as e:
            raise ValueError("Error parsing {0}: {1}".format(self.path, e))

        osgroup = None
        for key, section in config.items():
            if "commands" in section:
                if key in self.servers:
                    raise ValueError("Duplicate OS group name: {0}".format(key))
                osgroup = OperatingSystemGroup(self, key, **section)
                self.operating_systems.append(osgroup)
                self.servers.extend(osgroup.servers)
            else:
                if osgroup is None:
                    raise ValueError("Server in configuration file but no OS group defined yet")
                self.servers.append(Server(osgroup, section))

        return

    def save(self):
        config = ConfigObj()

        for osgroup in self.operating_systems:
            section = {"commands": osgroup.update_commands}

            if osgroup.description is not None and osgroup.description != "":
                section["description"] = osgroup.description

            if osgroup.servers:
                section["servers"] = osgroup.servers

            if osgroup.connect_command != DEFAULT_CONNECT_COMMAND:
                section["connect"] = osgroup.connect_command

            if osgroup.command_separator != DEFAULT_COMMAND_SEPARATOR:
                section["command_separator"] = osgroup.command_separator

            config[osgroup.name] = section
            for server in osgroup.servers:
                if server.description:
                    config[server.name] = {"description": server.description}

        self.log.debug("Saving configuration to {0}".format(self.path))
        config.write(outfile=open(self.path, "w"))

    def match_os(self, name):
        for os in self.operating_systems:
            if os.name == name:
                return os

        raise ValueError("Unknown OS: {0}".format(name))
Esempio n. 45
0
 def __init__(self, osgroup, name, description=None):
     self.log = Logger().default_stream
     self.osgroup = osgroup
     self.name = name
     self.description = description
Esempio n. 46
0
 def __init__(self, authorized_keys=DEFAULT_AUTHORIZED_KEYS):
     self.log = Logger().default_stream
     self.__parse_user_keyfiles()
     self.authorized_keys = AuthorizedKeys(authorized_keys)
Esempio n. 47
0
class UserSSHKeys(dict):
    """
    List of user configured SSH keys to process
    """
    def __init__(self, authorized_keys=DEFAULT_AUTHORIZED_KEYS):
        self.log = Logger().default_stream
        self.__parse_user_keyfiles()
        self.authorized_keys = AuthorizedKeys(authorized_keys)

    def __parse_user_keyfiles(self):
        """
        Parses any normal user keyfiles in ~/.ssh directory automatically.

        These keys are marked by default not to be automatically loaded:
        to enable, add key path to ~/.ssh/sshkeys.conf
        """

        user_sshdir = os.path.expanduser('~/.ssh')
        if not os.path.isdir(user_sshdir):
            return

        paths = []
        for filename in os.listdir(user_sshdir):
            if filename in SSH_CONFIG_FILES or os.path.splitext(filename)[1] != '.pub':
                continue
            path = os.path.join(user_sshdir, filename)
            if os.path.isfile(path):
                paths.append(path)
        for path in paths:
            try:
                sshkey = SSHKeyFile(self, path)
            except SSHKeyError as e:
                self.log.debug(e)
                continue

            self[sshkey.path] = sshkey

    def read_config(self, path):
        """
        Read a key configuration file, containing list of keys outside
        ~/.ssh directory to process.
        """
        if not os.path.isfile(path):
            raise SSHKeyError('No such file: {0}'.format(path))

        try:
            for l in [l.rstrip() for l in open(path, 'r').readlines()]:
                sshkey = SSHKeyFile(self, os.path.expandvars(os.path.expanduser(l)))

                if sshkey.path not in self.keys():
                    self[sshkey.path] = sshkey

                self[sshkey.path].autoload = True

        except IOError as e:
            raise SSHKeyError('Error loading {0}: {1}'.format(path, e))
        except OSError as e:
            raise SSHKeyError('Error loading {0}: {1}'.format(path, e))

    @property
    def loaded_keys(self):
        """
        Return fingerprint, key mapping of keys loaded to ssh-agent
        """

        keys = {}

        cmd = ['ssh-add', '-l']
        p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
        (stdout, stderr) = [x.decode('utf-8') for x in p.communicate()]

        if p.returncode == 1:
            l = stdout.split('\n')[0].strip()
            if l == 'The agent has no identities.':
                return keys

        if p.returncode != 0:
            raise SSHKeyError('Error listing loaded SSH agent keys')

        for l in [l.rstrip() for l in stdout.split('\n')[:-1]]:
            data = parse_public_key_line_pattern(l)
            if not data:
                raise SSHKeyError('Error parsing agent key list line {0}'.format(l))
            keys[data['fingerprint']] = data

        return keys

    @property
    def available(self):
        return [k for k in self.values() if k.available]

    def keys(self):
        """
        Return key filenames as sorted list
        """
        return sorted(super(UserSSHKeys, self).keys())

    def items(self):
        """
        Return (filename, key) value pairs sorted with self.keys()
        """
        return [(k, self[k]) for k in self.keys()]

    def values(self):
        """
        Return keys sorted with self.keys()
        """
        return [self[k] for k in self.keys()]

    def fix_permissions(self, directory_permissions=SSH_DIR_PERMS, file_permissions=SSH_FILE_PERMS):
        """
        Fix permissions in ~/.ssh to match permissions set by parameters. All files must be
        writable by user to use this function.

        Default values:
        directory_permissions   Directory permissions, by default '0700'
        file_permissions        File permissions, bt default '0600'
        """
        ssh_dir = os.path.expanduser('~/.ssh')
        dperm = int(directory_permissions, 8)
        fperm = int(file_permissions, 8)

        if not os.path.isdir(ssh_dir):
            self.log.debug('No such directory: {0}'.format(ssh_dir))
            return

        for (root, dirs, files) in os.walk(ssh_dir):
            if stat.S_IMODE(os.stat(root).st_mode) != dperm:
                self.log.debug('Fixing permissions for directory {0}'.format(root))
                os.chmod(root, dperm)

            for f in [os.path.join(root, f) for f in files]:
                if stat.S_IMODE(os.stat(f).st_mode) != fperm:
                    self.log.debug('Fixing permissions for file {0}'.format(f))
                    os.chmod(f, fperm)

    def load_keys(self, keys):
        """Load given keys

        Load given keys to SSH agent. Checks if key was already loaded and skips
        if it was. Keys can be either paths or SSHKeyFile instances.
        """
        paths = []
        for key in keys:
            if isinstance(key, SSHKeyFile):
                if not key.is_loaded:
                    paths.append(key.path)
            elif isinstance(key, str):
                paths.append(key)

        if paths:
            self.log.debug('Loading {0:d} keys to SSH agent'.format(len(paths)))
            cmd = [ 'ssh-add'] + paths
            p = Popen(cmd, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr)
            p.wait()
        else:
            self.log.debug('All SSH keys were already loaded to SSH agent')
Esempio n. 48
0
class OIDPrefix(object):
    """Sortable OID base class

    Base class for OID prefixes, sortable in OID order

    """
    def __init__(self, oid):
        self.log = Logger('snmp').default_stream
        self.oid = self.__format_oid__(oid)
        self.oid_string = self.__format_oid_string__(oid)

        self._parent = None
        self._next = None

    def __format_oid_string__(self, oid):
        return '.%s' % '.'.join(str(x) for x in self.__format_oid__(oid))

    def __format_oid__(self, oid):
        """Format OID

        Format OID string to validated OID list

        Return list of integeres
        """

        if isinstance(oid, basestring):
            try:
                oid = oid.strip('.').split('.')
            except ValueError:
                raise ValueError('Invalid OID: %s' % oid)

        return self.__validate_oid__(oid)

    def __validate_oid__(self, oid):
        """Validate OID and return

        Validate list of integers to be a valid OID

        Returns value if OK
        """
        try:
            oid = [int(x) for x in oid]
            for x in oid:
                if x<=0:
                    raise ValueError

        except ValueError:
            raise ValueError('Invalid OID: %s' % oid)

        return oid

    def __invalid__(self, message):
        """Invalid request

        Log error and return None

        """
        self.log.debug('%s' % message)
        return None

    def __cmp__(self, other):
        """Compare oid prefix items

        Compares OID prefixes

        """
        if isinstance(other, basestring):
            oid = self.__format_oid__(other)

        elif isinstance(other, OIDPrefix):
            oid = other.oid

        else:
            raise ValueError('Error comparing OIDPrefix to %d' % type(other))

        if len(oid) != len(self.oid):
            return cmp(len(self.oid), len(oid))

        for i in range(0, len(oid)):
            if oid[i] != self.oid[i]:
                return cmp(self.oid[i], oid[i])

        return cmp(self.oid, oid)

    def __eq__(self, oid):
        return self.__cmp__(oid) == 0

    def __ne__(self, oid):
        return self.__cmp__(oid) != 0

    def __lt__(self, oid):
        return self.__cmp__(oid) < 0

    def __le__(self, oid):
        return self.__cmp__(oid) <= 0

    def __gt__(self, oid):
        return self.__cmp__(oid) > 0

    def __ge__(self, oid):
        return self.__cmp__(oid) >= 0

    @property
    def get_response(self):
        return None

    @property
    def next_response(self):
        return None

    @property
    def parent(self):
        return self._parent
    @parent.setter
    def parent(self, value):
        self._parent = value

    @property
    def next(self):
        return self._next
    @next.setter
    def next(self, value):
        self._next = value

    def match(self, other):
        """Match other OID

        Match OID prefixes. Default function checks if self.oid == other.oid

        Override in child class (for example, trees match tree prefix)
        """
        return self == other
Esempio n. 49
0
 def __init__(self, path=DEFAULT_AUTHORIZED_KEYS):
     self.log = Logger().default_stream
     self.path = path
     self.load()
Esempio n. 50
0
class SNMPAgent(Script):
    """SNMP Agent implementation

    SNMP agent daemon for net-snmpd pass_persist mode

    Example usage:

        agent = SNMPAgent('.1.2.3.4')
        tree = agent.register_tree('1.2.3.4.1')
        tree.register('1.2.3.4.1.1', 'integer', 1)
        tree.register('1.2.3.4.1.2', 'integer', 2)

        agent.register_tree('1.2.3.4.2')
        agent.register('1.2.3.4.2.1', 'integer', 1)
        agent.register('1.2.3.4.2.4', 'integer', 4)
        agent.register('1.2.3.4.2.3', 'integer', 3)
        agent.register('1.2.3.4.2.2', 'integer', 2)

        tree = agent.register_tree('1.2.3.4.3')
        tree.register('1.2.3.4.3.1.1', 'integer', 1)
        tree.add_values('string', [x for x in string.digits[2:]])

        tree = agent.register_tree('1.2.3.4.4')
        tree.add_prefix_map([
            { 'oid': '1.2.3.4.4.1', 'key': 'string', 'values': [x for x in string.letters[:10]]},
            { 'oid': '1.2.3.4.4.2', 'key': 'integer', 'values': [x for x in string.digits[:10]]},
        ])

        agent.run()
    """
    def __init__(self, oid, reload_interval=60):
        Script.__init__(self)
        self.log = Logger('snmp').default_stream

        self.reload_interval = reload_interval
        self.last_reload = None

        self.add_argument('-g', '--get', help='SNMP GET request')
        self.add_argument('-n', '--next', help='SNMP GET request')
        self.add_argument('-t', '--tree', action='store_true', help='Show OID tree')

        self.tree = Tree(oid)

    def __SIGHUP__(self, signum, frame):
        """
        Signal handler to reload configuration. Note this requires also the
        IOError processing in main input loop below
        """
        self.log.debug('Reloading from signal')
        self.reload()

    def register_tree(self, oid):
        return self.tree.add(Tree(oid))

    def register(self, oid, key, value):
        return self.tree.add(Item(oid, key, value))

    def GET(self, oid):
        """GET from agent

        Return given OID from agent tree or None

        """
        try:
            return self.tree.GET(oid)
        except SNMPError, emsg:
            self.log.debug(emsg)
            return None
Esempio n. 51
0
class OperatingSystemGroup(object):
    """
    Group of operating systems in configuration file
    """

    def __init__(self, config, name, **kwargs):
        self.log = Logger().default_stream
        self.name = name
        self.description = name
        self.connect_command = DEFAULT_CONNECT_COMMAND
        self.command_separator = DEFAULT_COMMAND_SEPARATOR
        self.update_commands = []
        self.servers = []

        for k in ( 'description', 'command_separator', ):
            if k in kwargs:
                setattr(self, k, kwargs[k])

        if 'connect' in kwargs:
            self.connect_command = kwargs['connect']

        if 'commands' in kwargs:
            self.update_commands = kwargs['commands']

        if 'servers' in kwargs:
            names = kwargs['servers']
            if not isinstance(names, list):
                names = [names]

            for name in names:
                self.servers.append(Server(self, name))

        self.modified = False

    def __repr__(self):
        return '%s: %s (%d servers)' % (self.name,self.description,len(self.servers))

    @property
    def command_separator(self):
        return self._command_separator
    @command_separator.setter
    def command_separator(self, value):
        self._command_separator = value
        self.modified = True

    @property
    def connect_command(self):
        return self._connect_command
    @connect_command.setter
    def connect_command(self, value):
        if isinstance(value, basestring):
            value = value.split()
        self._connect_command = value
        self.modified = True

    @property
    def update_command(self):
        return self._update_commands
    @update_command.setter
    def update_commands(self, value):
        if isinstance(value, basestring):
            value = [value]
        self._update_commands = value
        self.modified = True

    @property
    def descripion(self):
        return self._descripion
    @descripion.setter
    def descripion(self, value):
        self._descripion = value
        self.modified = True

    def add_server(self, name):
        if name in [s.name for s in self.servers]:
            self.log.debug('Error adding: server already in group: %s' % name)
            return

        self.servers.append(Server(self, name))
        self.modified = True

    def remove_server(self,name):
        try:
            server = [s for s in self.servers if s.name==name][0]
            self.servers.remove(server)
            self.modified = True
        except IndexError:
            self.log.debug('Error removing: server not in group: %s' % name)
            return
Esempio n. 52
0
 def __init__(self, path=None):
     self.defaults = {}
     self.log = Logger().default_stream
     self.path = path is not None and path or os.path.expanduser('~/.ssh/config')
     self.reload()
Esempio n. 53
0
class Server(object):
    """
    Configuration for one server
    """
    def __init__(self, osgroup, name, description=None):
        self.log = Logger().default_stream
        self.osgroup = osgroup
        self.name = name
        self.description = description

    def __repr__(self):
        if self.description is not None:
            return '%s (%s)' % (self.name, self.description)
        else:
            return '%s' % self.name

    def __cmp__(self, other):
        if isinstance(other, basestring):
            return cmp(self.name, other)

        return cmp(self, other)

    def __eq__(self, other):
        return self.__cmp__(other) == 0

    def __ne__(self, other):
        return self.__cmp__(other) != 0

    def __gt__(self, other):
        return self.__cmp__(other) > 0

    def __gte__(self, other):
        return self.__cmp__(other) >= 0

    def __lt__(self, other):
        return self.__cmp__(other) < 0

    def __lte__(self, other):
        return self.__cmp__(other) <= 0

    @property
    def connect_command(self):
        return [x=='SERVER' and self.name or x for x in self.osgroup.connect_command]

    def check_output(self, command):
        """
        Wrapper to execute and check output of a command without
        interactive console actions

        Splits string automatically to words so pass a list if you
        need to escape stuff properly.
        """
        if not isinstance(command, list):
            command = [command]

        cmd = self.connect_command + command
        p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
        (stdout,stderr) = p.communicate()
        return p.returncode, stdout.rstrip('\n'), stderr.rstrip('\n')

    def shell(self, command):
        """
        Wrapper to run interactive shell command on the host, using
        current terminal for stdin, stdout and stderr

        Splits string automatically to words so pass a list if you
        need to escape stuff properly.

        Returns command return code
        """
        if not isinstance(command, list):
            command = [command]

        cmd = self.connect_command + command
        p = Popen(cmd, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr)
        p.wait()
        return p.returncode

    def update(self):
        """
        Wrapper to call correct update commands for this host
        """
        if not self.osgroup.update_commands:
            self.log.debug('No update commands for OS %s' % self.osgroup.name)
            return

        self.log.debug("Running: %s '%s'" % (
            ' '.join(self.connect_command),
            self.osgroup.command_separator.join(self.osgroup.update_commands)
        ))

        cmd = self.connect_command + [
            self.osgroup.command_separator.join(self.osgroup.update_commands)
        ]
        p = Popen(cmd, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr)
        p.wait()
        return p.returncode

    def remove(self):
        """Remove from configuration

        Remove this server from configuration

        """
        return self.osgroup.remove_server(self)
Esempio n. 54
0
import json
import logging

from dateutil.parser import parse as dateparser
from seine.whois import WhoisError
from seine.address import IPv4Address, IPv6Address
from systematic.log import Logger

URL_TEMPLATE = 'http://whois.arin.net/rest/ip/%(address)s'

DATE_FIELD_MAP = {
    'registered': 'registrationDate',
    'updated': 'updateDate',
}

logger = Logger().default_stream

class ARINNetBlock(object):
    """Netblock entry

    Netblock entry in ARINReverseIP results

    """
    def __init__(self, reverse, data):
        self.reverse = reverse
        self.description = data['description']['$']
        self.type = data['type']['$']
        self.mask = int(data['cidrLength']['$'])

        if reverse.address_format == IPv4Address:
            self.start = IPv4Address(data['startAddress']['$'])
Esempio n. 55
0
class Script(object):
    """
    Class for common CLI tool script
    """
    def __init__(self, name=None, description=None, epilog=None, debug_flag=True):
        self.name = os.path.basename(sys.argv[0])
        setproctitle('%s %s' % (self.name, ' '.join(sys.argv[1:])))
        signal.signal(signal.SIGINT, self.SIGINT)

        reload(sys)
        sys.setdefaultencoding('utf-8')

        if name is None:
            name = self.name

        # Set to True to avoid any messages from self.message to be printed
        self.silent = False

        self.logger = Logger(self.name)
        self.log = self.logger.default_stream

        self.subcommand_parser = None
        self.parser = argparse.ArgumentParser(
            prog=name,
            description=description,
            formatter_class=argparse.RawTextHelpFormatter,
            epilog=epilog,
            add_help=True,
            conflict_handler='resolve',
        )
        if debug_flag:
            self.parser.add_argument('--debug', action='store_true', help='Show debug messages')

        self.parser.add_argument('--insecure', action='store_false', help='No HTTPS certificate validation')
        self.parser.add_argument('-B', '--browser',
            choices=('chrome','chromium','firefox'),
            help='Browser for cookie stealing'
        )

    def SIGINT(self, signum, frame):
        """
        Parse SIGINT signal by quitting the program cleanly with exit code 1
        """
        for t in filter(lambda t: t.name!='MainThread', threading.enumerate()):
            t.stop()
        for t in filter(lambda t: t.name!='MainThread', threading.enumerate()):
            t.join()
        self.exit(1)

    def wait(self, poll_interval=1):
        """
        Wait for running threads to finish.
        Poll interval is time to wait between checks for threads
        """
        while True:
            active = filter(lambda t: t.name!='MainThread', threading.enumerate())
            if not len(active):
                break
            self.log.debug('Waiting for %d threads' % len(active))
            time.sleep(poll_interval)

    def exit(self, value=0, message=None):
        """
        Exit the script with given exit value.
        If message is not None, it is printed on screen.
        """
        if isinstance(value, bool):
            if value:
                value = 0
            else:
                value = 1
        else:
            try:
                value = int(value)
                if value < 0 or value > 255:
                    raise ValueError
            except ValueError:
                value = 1

        if message is not None:
            self.message(message)

        for t in filter(lambda t: t.name!='MainThread', threading.enumerate()):
            t.stop()

        while True:
            active = filter(lambda t: t.name!='MainThread', threading.enumerate())
            if not len(active):
                break
            time.sleep(1)

        sys.exit(value)

    def message(self, message):
        if self.silent:
            return
        sys.stdout.write('%s\n' % message)

    def error(self, message):
        sys.stderr.write('%s\n' % message)

    def add_subcommand(self, command):
        """Add a subcommand parser instance

        Register named subcommand parser to argument parser

        Subcommand parser must be an instance of ScriptCommand class.

        Example usage:

        class ListCommand(ScriptCommand):
            def run(self, args):
                print 'Listing stuff'

        parser.add_subcommand(ListCommand('list', 'List stuff from script'))

        """

        if self.subcommand_parser is None:
            self.subcommand_parser = self.parser.add_subparsers(
                dest='command', help='Please select one command mode below',
                title='Command modes'
            )
            self.subcommands = {}

        if not isinstance(command, ScriptCommand):
            raise ScriptError('Subcommand must be a ScriptCommand instance')

        parser = self.subcommand_parser.add_parser(
            command.name,
            help=command.short_description,
            description=command.description,
            epilog=command.epilog,
            formatter_class=argparse.RawTextHelpFormatter,
        )
        self.subcommands[command.name] = command
        command.script = self

        return parser

    def usage_error(self, *args, **kwargs):
        return self.parser.error(*args, **kwargs)

    def add_argument(self, *args, **kwargs):
        """
        Shortcut to add argument to main argumentparser instance
        """
        self.parser.add_argument(*args, **kwargs)

    def parse_args(self):
        """
        Call parse_args for parser and check for default logging flags
        """
        args = self.parser.parse_args()

        if hasattr(args, 'debug') and getattr(args, 'debug'):
            self.logger.set_level('DEBUG')

        elif hasattr(args, 'quiet') and getattr(args, 'quiet'):
            self.silent = True

        elif hasattr(args, 'verbose') and getattr(args, 'verbose'):
            self.logger.set_level('INFO')

        if self.subcommand_parser is not None:
            self.subcommands[args.command].run(args)

        return args

    def execute(self, args, dryrun=False):
        """
        Default wrapper to execute given interactive shell command
        with standard stdin, stdout and stderr
        """
        if isinstance(args, basestring):
            args = args.split()

        if not isinstance(args, list):
            raise ValueError('Execute arguments must be a list')

        if dryrun:
            self.log.debug('would execute: %s' % ' '.join(args))
            return 0

        p = Popen(args, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr)
        p.wait()
        return p.returncode

    def check_output(self, args):
        """
        Wrapper for subprocess.check_output to be executed in script context
        """
        if isinstance(args, basestring):
            args = [args]
        try:
            return check_output(args)

        except IOError, (ecode, emsg):
            raise ScriptError(emsg)

        except OSError, (ecode, emsg):
            raise ScriptError(emsg)
Esempio n. 56
0
 def __init__(self, osgroup, name, description=None):
     self.log = Logger().default_stream
     self.osgroup = osgroup
     self.name = name
     self.description = description
Esempio n. 57
0
 def __init__(self, path=DEFAULT_KNOWN_HOSTS, fingerprint_hash=None):
     self.log = Logger().default_stream
     self.path = path
     self.fingerprint_hash = fingerprint_hash
     self.load()
Esempio n. 58
0
class Script(object):
    """
    Class for common CLI tool script
    """
    def __init__(self,
                 name=None,
                 description=None,
                 epilog=None,
                 debug_flag=True):
        self.name = os.path.basename(sys.argv[0])
        signal.signal(signal.SIGINT, self.SIGINT)

        if has_setproctitle:
            setproctitle('{0} {1}'.format(self.name, ' '.join(sys.argv[1:])))

        if sys.version_info.major < 3:
            reload(sys)  # noqa
            sys.setdefaultencoding('utf-8')

        if name is None:
            name = self.name

        # Set to True to avoid any messages from self.message to be output
        self.silent = False

        self.logger = Logger(self.name)
        self.log = self.logger.default_stream

        self.parser = argparse.ArgumentParser(
            prog=name,
            description=description,
            formatter_class=argparse.RawTextHelpFormatter,
            epilog=epilog,
            add_help=True,
            conflict_handler='resolve',
        )

        if debug_flag:
            self.parser.add_argument('--debug',
                                     action='store_true',
                                     help='Show debug messages')

        self.subcommand_parser = None

    def SIGINT(self, signum, frame):
        """
        Parse SIGINT signal by quitting the program cleanly with exit code 1
        """
        for t in [t for t in threading.enumerate() if t.name != 'MainThread']:
            if hasattr(t, 'stop') and callable(t.stop):
                t.stop()

        for t in [t for t in threading.enumerate() if t.name != 'MainThread']:
            t.join()

        self.exit(1)

    def wait(self, poll_interval=1):
        """
        Wait for running threads to finish.
        Poll interval is time to wait between checks for threads
        """
        while True:
            active = [
                t for t in threading.enumerate() if t.name != 'MainThread'
            ]
            if not len(active):
                break
            self.log.debug('Waiting for {0:d} threads'.format(len(active)))
            time.sleep(poll_interval)

    def exit(self, value=0, message=None):
        """
        Exit the script with given exit value.
        If message is not None, it is output to stdout
        """
        if isinstance(value, bool):
            if value:
                value = 0
            else:
                value = 1
        else:
            try:
                value = int(value)
                if value < 0 or value > 255:
                    raise ValueError
            except ValueError:
                value = 1

        if message is not None:
            self.message(message)

        for t in [t for t in threading.enumerate() if t.name != 'MainThread']:
            if hasattr(t, 'stop') and callable(t.stop):
                t.stop()

        while True:
            active = [
                t for t in threading.enumerate() if t.name != 'MainThread'
            ]
            if not len(active):
                break
            time.sleep(0.1)

        sys.exit(value)

    def message(self, message):
        if self.silent:
            return
        sys.stdout.write('{0}\n'.format(message))

    def error(self, message):
        sys.stderr.write('{0}\n'.format(message))

    def add_subcommand(self, command):
        """Add a subcommand parser instance

        Register named subcommand parser to argument parser

        Subcommand parser must be an instance of ScriptCommand class.

        Example usage:

        class ListCommand(ScriptCommand):
            def run(self, args):
                self.message('Listing stuff')

        parser.add_subcommand(ListCommand('list', 'List stuff from script'))

        """

        if self.subcommand_parser is None:
            self.subcommand_parser = self.parser.add_subparsers(
                dest='command',
                help='Please select one command mode below',
                title='Command modes')
            self.subcommands = {}

        if not isinstance(command, ScriptCommand):
            raise ScriptError('Subcommand must be a ScriptCommand instance')

        parser = self.subcommand_parser.add_parser(
            command.name,
            help=command.short_description,
            description=command.description,
            epilog=command.epilog,
            formatter_class=argparse.RawTextHelpFormatter,
        )
        self.subcommands[command.name] = command
        command.script = self

        if callable(getattr(command, '__register_arguments__', None)):
            command.__register_arguments__(parser)

        return parser

    def usage_error(self, *args, **kwargs):
        return self.parser.error(*args, **kwargs)

    def add_argument(self, *args, **kwargs):
        """
        Shortcut to add argument to main argumentparser instance
        """
        self.parser.add_argument(*args, **kwargs)

    def __process_args__(self, args):
        """Process args
        Process args from parse_*args CalledProcessError
        """
        if getattr(args, 'debug', None):
            self.logger.set_level('DEBUG')

        elif getattr(args, 'quiet', None):
            self.silent = True

        elif getattr(args, 'verbose', None):
            self.logger.set_level('INFO')

        if self.subcommand_parser is not None and args.command is not None:
            if hasattr(self.subcommands[args.command], 'parse_args'):
                args = self.subcommands[args.command].parse_args(args)
            self.subcommands[args.command].run(args)

        return args

    def parse_args(self):
        """
        Call parse_args for parser and check for default logging flags
        """
        return self.__process_args__(self.parser.parse_args())

    def parse_known_args(self):
        """
        Call parse_args for parser and check for default logging flags
        """
        args, other_args = self.parser.parse_known_args()
        args = self.__process_args__(args)
        return args, other_args

    def run(self):
        """
        Run script, parsing arguments and running subcommands

        This expects subcommands have been registered and exists if not

        This simply runs self.parse_args() not expecting to do anything with
        returned values since the subcommand is run.
        """
        if self.subcommand_parser is None:
            self.exit(1, 'Command defines no subcommands')

        args = self.parse_args()
        if args.command is None:
            self.exit(1, 'No command selected')

    def execute(self,
                args,
                stdin=sys.stdin,
                stdout=sys.stdout,
                stderr=sys.stderr,
                dryrun=False):
        """
        Default wrapper to execute given interactive shell command
        with standard stdin, stdout and stderr
        """
        if isinstance(args, str):
            args = args.split()

        if not isinstance(args, list):
            raise ValueError('Execute arguments must be a list')

        if dryrun:
            self.log.debug('would execute: {0}'.format(' '.join(args)))
            return 0

        p = Popen(args, stdin=stdin, stdout=stdout, stderr=stderr)
        p.wait()
        return p.returncode

    def check_output(self, args):
        """
        Wrapper for subprocess.check_output to be executed in script context
        """
        if isinstance(args, str):
            args = [args]
        try:
            return check_output(args)

        except IOError as e:
            raise ScriptError(e)

        except OSError as e:
            raise ScriptError(e)

        except CalledProcessError as e:
            raise ScriptError(e)
Esempio n. 59
0
 def __init__(self, resultset, name):
     self.log = Logger().default_stream
     self.resultset = resultset
     self.name = name
     self.ansible_facts = {}