Ejemplo n.º 1
0
 def test_get_sql_by_pg_version_should_return_newer_when_bigger_than_96(
         self):
     cluster = self.cluster.copy()
     cluster['ver'] = 9.7
     collector = PgStatCollector.from_cluster(cluster, 1049)
     self.assertEqual(SELECT_PGSTAT_NEVER_VERSION,
                      collector.get_sql_pgstat_by_version())
Ejemplo n.º 2
0
 def test_ncurses_produce_prefix_should_return_online_when_pgcon(
         self, mocked__status, mocked__max_conn):
     self.cluster['pgcon'].get_parameter_status.return_value = '9.3'
     collector = PgStatCollector.from_cluster(self.cluster, [1049])
     self.assertEqual(
         '/var/lib/postgresql/9.3/main 9.3 role connections: 0 of 10 allocated, 0 active\n',
         collector.ncurses_produce_prefix())
Ejemplo n.º 3
0
 def test_get_additional_info_should_update_when_not_backend_and_not_action(
         self, mocked__get_memory_usage, mocked__get_psinfo):
     collector = PgStatCollector.from_cluster(self.cluster, 1049)
     mocked__get_psinfo.return_value = ('vacuum', None)
     mocked__get_memory_usage.return_value = 10
     info = collector.get_additional_proc_info(1049, {'cmdline': ''}, [10])
     self.assertEqual({'type': 'vacuum', 'cmdline': '', 'uss': 10}, info)
Ejemplo n.º 4
0
 def test__get_psinfo_should_return_pstype_action_when_cmdline_matches_postgres_process(
         self):
     collector = PgStatCollector.from_cluster(self.cluster, 1049)
     pstype, action = collector._get_psinfo(
         'postgres: checkpointer process')
     self.assertEqual('checkpointer', pstype)
     self.assertEqual('', action)
Ejemplo n.º 5
0
 def test__get_psinfo_should_return_pstype_action_when_cmdline_matches_autovacuum_worker(
         self):
     collector = PgStatCollector.from_cluster(self.cluster, 1049)
     pstype, action = collector._get_psinfo(
         'postgres: autovacuum worker process')
     self.assertEqual('autovacuum', pstype)
     self.assertEqual('', action)
Ejemplo n.º 6
0
 def test_get_subprocesses_pid_should_return_empty_when_no_cmd_output(
         self, mocked_logger, mocked_process):
     mocked_process.return_value.children.return_value = []
     collector = PgStatCollector.from_cluster(self.cluster, 1049)
     self.assertEqual([], collector.get_subprocesses_pid())
     mocked_logger.info.assert_called_with(
         "Couldn't determine the pid of subprocesses for 1049")
Ejemplo n.º 7
0
 def test_get_sql_by_pg_version_should_return_less_than_96_when_dbver_95(
         self):
     cluster = self.cluster.copy()
     cluster['ver'] = 9.5
     collector = PgStatCollector.from_cluster(cluster, 1049)
     self.assertEqual(SELECT_PGSTAT_VERSION_LESS_THAN_96,
                      collector.get_sql_pgstat_by_version())
Ejemplo n.º 8
0
    def test__read_pg_stat_activity_should_parse_pg_stats_when_ok(self):
        results = [{
            'datname': 'postgres',
            'client_addr': None,
            'locked_by': None,
            'pid': 11139,
            'waiting': False,
            'client_port': -1,
            'query': 'idle',
            'age': None,
            'usename': 'postgres'
        }]

        self.cluster[
            'pgcon'].cursor.return_value.fetchall.return_value = results
        collector = PgStatCollector.from_cluster(self.cluster, [1049])
        activity_stats = collector._read_pg_stat_activity()
        expected_stats = {
            11139: {
                'datname': 'postgres',
                'client_addr': None,
                'locked_by': None,
                'pid': 11139,
                'waiting': False,
                'client_port': -1,
                'query': 'idle',
                'age': None,
                'usename': 'postgres'
            }
        }

        self.assertEqual(expected_stats, activity_stats)
Ejemplo n.º 9
0
 def test_query_status_fn_should_return_ok_when_no_warning(self):
     collector = PgStatCollector.from_cluster(self.cluster, 1049)
     formatter = StatusFormatter(collector)
     row = [
         11139, None, 'backend', None, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.9, '21:05:45', 'postgres',
         'postgres', False, 'ok'
     ]
     col = {'warning': 'idle in transaction', 'critical': 'locked', 'out': 'query'}
     self.assertEqual({-1: 0}, formatter.query_status_fn(row, col))
Ejemplo n.º 10
0
 def test_age_status_fn_should_return_ok_when_age_less_than_warning(self):
     collector = PgStatCollector.from_cluster(self.cluster, 1049)
     formatter = StatusFormatter(collector)
     row = [
         11139, None, 'backend', None, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.9, '0:04:05',
         'postgres', 'postgres', False, 'idle in transaction for 20:51:17'
     ]
     col = {'warning': 300, 'critical': 500, 'out': 'age'}
     self.assertEqual({-1: 0}, formatter.age_status_fn(row, col))
Ejemplo n.º 11
0
 def test_get_subprocesses_pid_should_return_subprocesses_when_children_processes(
         self, mocked_process):
     subprocesses = [
         mock.Mock(pid=1051),
         mock.Mock(pid=1052),
         mock.Mock(pid=1206)
     ]
     mocked_process.return_value.children.return_value = subprocesses
     collector = PgStatCollector.from_cluster(self.cluster, 1049)
     self.assertEqual([1051, 1052, 1206], collector.get_subprocesses_pid())
Ejemplo n.º 12
0
 def test_get_additional_info_should_update_when_backend_and_active_pid_in_track_pids(
         self, mocked__get_memory_usage, mocked__get_psinfo):
     collector = PgStatCollector.from_cluster(self.cluster, [1049])
     mocked__get_psinfo.return_value = ('vacuum', None)
     mocked__get_memory_usage.return_value = 10
     info = collector.get_additional_proc_info(1049, {'cmdline': ''},
                                               {1049: {
                                                   'query': 'idle'
                                               }})
     self.assertEqual({'type': 'backend', 'cmdline': '', 'uss': 10}, info)
Ejemplo n.º 13
0
    def test_refresh_should_return_results_when_ok(
            self, mocked__do_refresh, mocked_get_proc_data,
            mocked_get_subprocesses_pid, mocked__read_pg_stat_activity,
            mocked___get_memory_usage):
        mocked_get_proc_data.return_value = {
            'read_bytes': 655,
            'write_bytes': 1,
            'pid': 1049,
            'status': 'status',
            'utime': 0.0002,
            'stime': 0.0001,
            'rss': 432,
            'priority': 10,
            'vsize': 252428288,
            'guest_time': 0.0,
            'starttime': 911,
            'delayacct_blkio_ticks': 1,
            'cmdline': 'backend'
        }

        mocked__read_pg_stat_activity.return_value = {
            11139: {
                'datname': 'postgres',
                'client_addr': None,
                'locked_by': None,
                'pid': 11139,
                'waiting': False,
                'client_port': -1,
                'query': 'idle',
                'age': None,
                'usename': 'postgres'
            }
        }

        collector = PgStatCollector.from_cluster(self.cluster, [1049])
        result = collector.refresh()
        expected_results = [{
            'status': 'status',
            'write_bytes': 1,
            'vsize': 252428288,
            'delayacct_blkio_ticks': 1,
            'pid': 1049,
            'priority': 10,
            'cmdline': 'backend',
            'read_bytes': 655,
            'uss': 10,
            'stime': 0.0001,
            'starttime': 911,
            'utime': 0.0002,
            'type': 'unknown',
            'guest_time': 0.0,
            'rss': 432
        }]
        self.assertEqual(expected_results, result)
        mocked__do_refresh.assert_called_with(result)
Ejemplo n.º 14
0
    def test_refresh_should_try_reconnect_whne_no_pgcon(
            self, mocked__do_refresh, mocked_get_proc_data,
            mocked_get_subprocesses_pid, mocked__read_pg_stat_activity,
            mocked___get_memory_usage, mocked_try_reconnect):
        mocked_get_proc_data.return_value = {}
        mocked__read_pg_stat_activity.return_value = {}

        collector = PgStatCollector.from_cluster(self.cluster, [1049])
        collector.pgcon = None
        result = collector.refresh()
        mocked_try_reconnect.assert_called_with()
        mocked__do_refresh.assert_called_with(result)
Ejemplo n.º 15
0
    def test_refresh_should_return_none_when_try_reconnect_raises_error(
            self, mocked__do_refresh, mocked_get_proc_data,
            mocked_get_subprocesses_pid, mocked__read_pg_stat_activity,
            mocked___get_memory_usage, mocked_try_reconnect):
        mocked_get_proc_data.return_value = {}
        mocked__read_pg_stat_activity.return_value = {}

        collector = PgStatCollector.from_cluster(self.cluster, [1049])
        collector.pgcon = None
        mocked_try_reconnect.side_effect = psycopg2.OperationalError
        result = collector.refresh()
        self.assertIsNone(result)
        mocked__do_refresh.assert_called_with([])
Ejemplo n.º 16
0
 def test__get_memory_usage_should_return_uss_when_memory_info_ok(
         self, mocked_psutil_process):
     mocked_psutil_process.return_value.memory_info.return_value = pmem(
         rss=1769472,
         vms=252428288,
         shared=344064,
         text=5492736,
         lib=0,
         data=1355776,
         dirty=0)
     collector = PgStatCollector.from_cluster(self.cluster, 1049)
     memory_usage = collector._get_memory_usage(1049)
     self.assertEqual(1425408, memory_usage)
Ejemplo n.º 17
0
 def test__read_proc_should_return_data_when_process_ok(
         self, mocked_psutil_process, mocked_os):
     collector = PgStatCollector.from_cluster(self.cluster, 1049)
     mocked_os.sysconf.return_value.SC_PAGE_SIZE = 4096
     mocked_process = mocked_psutil_process.return_value
     mocked_process.pid = 1049
     mocked_process.status.return_value = 'status'
     mocked_process.io_counters.return_value = pio(read_count=12,
                                                   write_count=13,
                                                   read_bytes=655,
                                                   write_bytes=1)
     cpu_times = pcputimes(user=0.02,
                           system=0.01,
                           children_user=0.0,
                           children_system=0.0)
     memory_info = pmem(rss=1769472,
                        vms=252428288,
                        shared=344064,
                        text=5492736,
                        lib=0,
                        data=1355776,
                        dirty=0)
     mocked_process.cpu_times.return_value = cpu_times
     mocked_process.memory_info.return_value = memory_info
     mocked_process.nice.return_value = '10'
     mocked_process.cmdline.return_value = ['backend \n']
     mocked_process.create_time.return_value = 1480777289.0
     proc_stats = collector.get_proc_data(1048)
     expected_proc_stats = {
         'read_bytes': 655,
         'write_bytes': 1,
         'pid': 1049,
         'state': 'status',
         'utime': 0.0002,
         'stime': 0.0001,
         'rss': 432,
         'priority': 10,
         'vsize': 252428288,
         'guest_time': 0.0,
         'starttime': datetime.datetime.fromtimestamp(1480777289.0),
         'delayacct_blkio_ticks': 0,
         'cmdline': 'backend'
     }
     self.assertEqual(expected_proc_stats, proc_stats)
Ejemplo n.º 18
0
 def test__get_recovery_status_should_return_zero_when_output_ok(
         self, mocked_execute_fetchone_query):
     collector = PgStatCollector.from_cluster(self.cluster, 1049)
     self.assertEqual('role', collector._get_recovery_status())
     mocked_execute_fetchone_query.assert_called_with(
         SELECT_PG_IS_IN_RECOVERY)
Ejemplo n.º 19
0
 def test_kb_pretty_print_should_return_formatted_when_kb(self):
     collector = PgStatCollector.from_cluster(self.cluster, 1049)
     formatter = FnFormatter(collector)
     formatted_kb = formatter.kb_pretty_print(1024)
     self.assertEqual('1024KB', formatted_kb)
Ejemplo n.º 20
0
 def test__get_psinfo_should_return_unknown_when_cmdline_not_match(self):
     collector = PgStatCollector.from_cluster(self.cluster, 1049)
     pstype, action = collector._get_psinfo('postgres1: worker process')
     self.assertEqual('unknown', pstype)
     self.assertIsNone(action)
Ejemplo n.º 21
0
 def test_time_interval_pretty_print_should_return_formatted_when_start_time_number(self):
     collector = PgStatCollector.from_cluster(self.cluster, 1049)
     formatter = FnFormatter(collector)
     formatted_time = formatter.time_pretty_print(68852.0)
     self.assertEqual('19:07:32', formatted_time)
Ejemplo n.º 22
0
 def test_time_interval_pretty_print_should_return_formatted_when_start_time_timedelta(self):
     collector = PgStatCollector.from_cluster(self.cluster, 1049)
     formatter = FnFormatter(collector)
     formatted_time = formatter.time_pretty_print(timedelta(seconds=30))
     self.assertEqual('00:30', formatted_time)
Ejemplo n.º 23
0
 def test__get_psinfo_should_return_pstype_action_when_cmdline_matches_postgres(
         self):
     collector = PgStatCollector.from_cluster(self.cluster, 1049)
     pstype, action = collector._get_psinfo('postgres: back')
     self.assertEqual('backend', pstype)
     self.assertIsNone(action)
Ejemplo n.º 24
0
 def test__get_psinfo_should_return_empty_when_no_cmdline(self):
     pstype, action = PgStatCollector.from_cluster(self.cluster,
                                                   1049)._get_psinfo('')
     self.assertEqual('unknown', pstype)
     self.assertIsNone(action)
Ejemplo n.º 25
0
 def test_time_interval_pretty_print_should_raise_error_when_non_valid_type(self):
     collector = PgStatCollector.from_cluster(self.cluster, 1049)
     formatter = FnFormatter(collector)
     with self.assertRaises(ValueError):
         formatter.time_pretty_print('None')
Ejemplo n.º 26
0
 def test_ncurses_produce_prefix_should_return_offline_when_no_pgcon(self):
     self.cluster['pgcon'].get_parameter_status.return_value = '9.3'
     collector = PgStatCollector.from_cluster(self.cluster, [1049])
     collector.pgcon = None
     self.assertEqual('/var/lib/postgresql/9.3/main 9.3 (offline)\n',
                      collector.ncurses_produce_prefix())
Ejemplo n.º 27
0
 def test_idle_format_fn_should_return_formatted_for_version_less_than_92(self):
     self.cluster['ver'] = 9.1
     collector = PgStatCollector.from_cluster(self.cluster, 1049)
     formatter = FnFormatter(collector)
     formatted_idle = formatter.idle_format_fn('idle in transaction 1')
     self.assertEqual('idle in transaction 00:01 since the last query start', formatted_idle)
Ejemplo n.º 28
0
 def test__get_max_connections_should_return_zero_when_output_ok(
         self, mocked_execute_fetchone_query):
     collector = PgStatCollector.from_cluster(self.cluster, 1049)
     self.assertEqual(1, collector._get_max_connections())
     mocked_execute_fetchone_query.assert_called_with(SHOW_MAX_CONNECTIONS)
Ejemplo n.º 29
0
def main():
    global options

    # bail out if we are not running Linux
    if platform.system() != 'Linux':
        print(
            'Non Linux database hosts are not supported at the moment. Can not continue'
        )
        sys.exit(243)

    options, args = parse_args()
    consts.TICK_LENGTH = options.tick
    output_method = options.output_method

    if not output_method_is_valid(output_method):
        print('Unsupported output method: {0}'.format(output_method))
        print('Valid output methods are: {0}'.format(','.join(
            get_valid_output_methods())))
        sys.exit(1)

    if output_method == OUTPUT_METHOD.curses and not curses_available:
        print(
            'Curses output is selected, but curses are unavailable, falling back to console output'
        )
        output_method = OUTPUT_METHOD.console

    log_stderr = setup_loggers(options)
    user_dbname = options.instance
    user_dbver = options.version
    clusters = []

    # now try to read the configuration file
    config = (read_configuration(options.config_file)
              if options.config_file else None)
    if config:
        for instance in config:
            if user_dbname and instance != user_dbname:
                continue
            # pass already aquired connections to make sure we only list unique clusters.
            db_client = DBClient.from_config(config[instance])
            try:
                cluster = db_client.establish_user_defined_connection(
                    instance, clusters)
            except (NotConnectedError, NoPidConnectionError):
                msg = 'failed to acquire details about the database cluster {0}, the server will be skipped'
                loggers.logger.error(msg.format(instance))
            except DuplicatedConnectionError:
                pass
            else:
                clusters.append(cluster)

    elif options.host:
        # try to connect to the database specified by command-line options
        instance = options.instance or "default"
        db_client = DBClient.from_options(options)
        try:
            cluster = db_client.establish_user_defined_connection(
                instance, clusters)
        except (NotConnectedError, NoPidConnectionError):
            loggers.logger.error(
                "unable to continue with cluster {0}".format(instance))
        except DuplicatedConnectionError:
            pass
        else:
            clusters.append(cluster)
    else:
        # do autodetection
        postmasters = ProcWorker().get_postmasters_directories()
        # get all PostgreSQL instances
        for result_work_dir, connection_params in postmasters.items():
            # if user requested a specific database name and version - don't try to connect to others
            try:
                validate_autodetected_conn_param(user_dbname, user_dbver,
                                                 result_work_dir,
                                                 connection_params)
            except InvalidConnectionParamError:
                continue
            db_client = DBClient.from_postmasters(result_work_dir,
                                                  connection_params.pid,
                                                  connection_params.version,
                                                  options)
            if db_client is None:
                continue
            conn = db_client.connection_builder.build_connection()
            try:
                pgcon = psycopg2.connect(**conn)
            except Exception as e:
                loggers.logger.error('PostgreSQL exception {0}'.format(e))
                pgcon = None
            if pgcon:
                desc = make_cluster_desc(name=connection_params.dbname,
                                         version=connection_params.version,
                                         workdir=result_work_dir,
                                         pid=connection_params.pid,
                                         pgcon=pgcon,
                                         conn=conn)
                clusters.append(desc)

    collectors = []
    groups = {}
    try:
        if not clusters:
            loggers.logger.error(
                'No suitable PostgreSQL instances detected, exiting...')
            loggers.logger.error(
                'hint: use -v for details, or specify connection parameters '
                'manually in the configuration file (-c)')
            sys.exit(1)

        # initialize the disks stat collector process and create an exchange queue
        q = JoinableQueue(1)
        work_directories = [cl['wd'] for cl in clusters if 'wd' in cl]

        collector = DetachedDiskStatCollector(q, work_directories)
        collector.start()
        consumer = DiskCollectorConsumer(q)

        collectors.append(HostStatCollector())
        collectors.append(SystemStatCollector())
        collectors.append(MemoryStatCollector())

        for cluster in clusters:
            partition_collector = PartitionStatCollector.from_cluster(
                cluster, consumer)
            pg_collector = PgStatCollector.from_cluster(cluster, options.pid)

            groups[cluster['wd']] = {
                'pg': pg_collector,
                'partitions': partition_collector
            }
            collectors.append(partition_collector)
            collectors.append(pg_collector)

        # we don't want to mix diagnostics messages with useful output, so we log the former into a file.
        loggers.logger.removeHandler(log_stderr)
        loop(collectors, consumer, groups, output_method)
        loggers.logger.addHandler(log_stderr)
    except KeyboardInterrupt:
        pass
    except curses.error:
        print(traceback.format_exc())
        if 'SSH_CLIENT' in os.environ and 'SSH_TTY' not in os.environ:
            print(
                'Unable to initialize curses. Make sure you supply -t option (force psedo-tty allocation) to ssh'
            )
    except:
        print(traceback.format_exc())
    finally:
        sys.exit(0)
Ejemplo n.º 30
0
 def test_idle_format_fn_should_return_formatted_for_version_bigger_than_92(self):
     collector = PgStatCollector.from_cluster(self.cluster, 1049)
     formatter = FnFormatter(collector)
     formatted_idle = formatter.idle_format_fn('idle in transaction 1')
     self.assertEqual('idle in transaction for 00:01', formatted_idle)