示例#1
0
def post_pg_control(http_context, app):
    # Control instance
    validate_parameters(http_context['post'], [
        ('action', T_CONTROL, False)
    ])
    action = http_context['post']['action']
    logger.info("PostgreSQL '%s' requested." % action)
    NotificationMgmt.push(app.config,
                          Notification(username=http_context['username'],
                                       message="PostgreSQL %s" % action))

    cmd = app.config.administration.pg_ctl % action
    cmd_args = oneline_cmd_to_array(cmd)
    (rcode, stdout, stderr) = exec_script(cmd_args)
    if rcode != 0:
        raise Exception(str(stderr))
    # Let's check if PostgreSQL is up & running after having executed
    # 'start' or 'restart' action.
    if action in ['start', 'restart']:
        # When a start/restart operation is requested, after the
        # startup/pg_ctl script has been executed then we check that
        # postgres is up & running:
        # while the PG conn. is not working then, for 10 seconds (max)
        # we'll check (connect/SELECT 1/disconnect) the connection, every
        # 0.5 second.
        retry = True
        t_start = time.time()
        while retry:
            try:
                with app.postgres.connect() as conn:
                    conn.execute('SELECT 1')
                    logger.info("Done.")
                    return dict(action=action, state='ok')
            except error:
                if (time.time() - t_start) > 10:
                    logger.info("Failed.")
                    return dict(action=action, state='ko')
            logger.info("Retrying...")
            time.sleep(0.5)

    elif action == 'stop':
        # Check the PG conn is not working anymore.
        try:
            retry = True
            t_start = time.time()
            while retry:
                with app.postgres.connect() as conn:
                    conn.execute('SELECT 1')
                time.sleep(0.5)
                if (time.time() - t_start) > 10:
                    retry = False
            logger.info("Failed.")
            return dict(action=action, state='ko')
        except error:
            logger.info("Done.")
            return dict(action=action, state='ok')

    elif action == 'reload':
        logger.info("Done.")
        return dict(action=action, state='ok')
示例#2
0
def post_hba_raw(conn, config, http_context):
    new_version = False

    # Push a notification.
    try:
        NotificationMgmt.push(
            config,
            Notification(username=http_context['username'],
                         message="HBA file updated"))
    except NotificationError as e:
        logger.error(e.message)

    if 'content' not in http_context['post']:
        raise HTTPError(406, "Parameter 'content' not sent.")
    if http_context and 'new_version' in http_context['post']:
        # Check parameter 'version'
        validate_parameters(http_context['post'],
                            [('new_version', T_NEW_VERSION, False)])
        if http_context['post']['new_version'] is True:
            new_version = True

    hba_file = get_setting(conn, 'hba_file')
    return HBAManager.save_file_content(hba_file,
                                        http_context['post']['content'],
                                        new_version)
示例#3
0
def get_settings(conn, http_context=None):
    filter_query = ''
    if http_context and 'filter' in http_context['query']:
        # Check 'filter' parameters.
        validate_parameters(http_context['query'],
                            [('filter', T_PGSETTINGS_FILTER, True)])
        filter = http_context['query']['filter'][0]
        filter_query = " WHERE name ILIKE '%{0}%'" \
                       " OR short_desc ILIKE '%{0}%'" \
                       " OR extra_desc ILIKE '%{0}%'".format(filter)
    query = """
SELECT
    name, setting, current_setting(name) AS current_setting, unit,
    vartype, min_val, max_val, enumvals, context, category,
    short_desc||' '||coalesce(extra_desc, '') AS desc, boot_val, reset_val,
    pending_restart
FROM pg_settings
%s ORDER BY category, name
    """ % (filter_query)
    ret = []
    for row in conn.query(query):
        if http_context and len(http_context['urlvars']) > 0:
            if http_context['urlvars'][0] != row['category']:
                continue
        cat_exists = False
        i = 0
        for ret_cat in ret:
            if ret_cat['category'] == row['category']:
                cat_exists = True
                break
            i += 1
        enumvals = row['enumvals']
        if enumvals is not None:
            # format enumvals as before switching from tpc to psycopg2
            enumvals = '{%s}' % ','.join(enumvals)
        row_dict = {
            'name': row['name'],
            'setting': row['setting'],
            'setting_raw': row['current_setting'],
            'unit': row['unit'],
            'vartype': row['vartype'],
            'min_val': row['min_val'],
            'max_val': row['max_val'],
            'boot_val': row['boot_val'],
            'reset_val': row['reset_val'],
            'enumvals': enumvals,
            'context': row['context'],
            'desc': row['desc'],
            'pending_restart': row['pending_restart'],
        }

        if not cat_exists:
            ret.append({'category': row['category'], 'rows': [row_dict]})
        else:
            ret[i]['rows'].append(row_dict)

    return ret
示例#4
0
def post_reindex(app, post, dbname, schema=None, table=None, index=None):
    # Parameters format validation
    if 'datetime' in post:
        validate_parameters(post, [
            ('datetime', T_TIMESTAMP_UTC, False),
        ])
    dt = post.get('datetime', datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"))

    with functions.get_postgres(app.config, dbname).connect() as conn:
        return functions.schedule_reindex(
            conn, dbname, dt, app, schema=schema, table=table, index=index)
示例#5
0
def api_vacuum(http_context, queue_in = None, config = None, sessions = None, commands = None):
    set_logger_name("administration")
    worker = b'vacuum'
    # Get a new logger.
    logger = get_logger(config)
    try:
        check_sessionid(http_context['headers'], sessions)
        post = http_context['post']
        # Check POST parameters.
        validate_parameters(post, [
            ('database', T_OBJECTNAME, False),
            ('table', T_OBJECTNAME, False),
            ('mode', T_VACUUMMODE, False)
        ])
        # Serialize parameters.
        parameters = base64.b64encode(
                        pickle.dumps({
                            'database': post['database'],
                            'table': post['table'],
                            'mode': post['mode']
        })).decode('utf-8')
    except (Exception, HTTPError) as e:
        logger.traceback(get_tb())
        logger.error(str(e))
        if isinstance(e, HTTPError):
            raise e
        else:
            raise HTTPError(500, "Internal error.")

    # Check command uniqueness.
    try:
        commands.check_uniqueness(worker, parameters)
    except SharedItem_exists as e:
        logger.traceback(get_tb())
        logger.error(str(e))
        raise HTTPError(402, "Vaccum '%s' already running on table '%s'." % (post['mode'], post['table']))
    cid =  hash_id(worker + b'-' + parameters.encode('utf-8'))
    command =  Command(
            cid.encode('utf-8'),
            time.time(),
            0,
            worker,
            parameters,
            0,
            u'')
    try:
        commands.add(command)
        # Put the Command in the command queue
        queue_in.put(command)
        return {"cid": cid}
    except SharedItem_no_free_slot_left as e:
        logger.traceback(get_tb())
        logger.error(str(e))
        raise HTTPError(500, "Internal error.")
示例#6
0
def check_sessionid(http_header, sessions):
    validate_parameters(http_header, [('X-Session', T_SESSIONID, False)])
    try:
        session = sessions.get_by_sessionid(
            http_header['X-Session'].encode('utf-8'))
        session.time = time.time()
        username = session.username
        sessions.update(session)
        return username
    except SharedItem_not_found:
        raise HTTPError(401, "Invalid session.")
示例#7
0
def post_hba(conn, config, http_context):
    new_version = False
    set_logger_name("settings")
    logger = get_logger(config)

    # Push a notification.
    try:
        NotificationMgmt.push(
            config,
            Notification(username=http_context['username'],
                         message="HBA file updated"))
    except NotificationError as e:
        logger.error(e.message)

    if 'entries' not in http_context['post']:
        raise HTTPError(406, "Parameter 'entries' not sent.")

    if http_context and 'new_version' in http_context['post']:
        # Check parameter 'version'
        validate_parameters(http_context['post'],
                            [('new_version', T_NEW_VERSION, False)])
        if http_context['post']['new_version'] is True:
            new_version = True

    hba_file = get_setting(conn, 'hba_file')
    hba_entries = []
    logger.debug(http_context['post']['entries'])
    for entry in http_context['post']['entries']:
        if 'comment' in entry and len(entry['connection']) == 0:
            new_hba_entry = HBAComment()
            new_hba_entry.comment = entry['comment']
        else:
            new_hba_entry = HBAEntry()
            try:
                new_hba_entry.connection = entry[
                    'connection'] if 'connection' in entry else ''
                new_hba_entry.database = entry[
                    'database'] if 'database' in entry else ''
                new_hba_entry.user = entry['user'] if 'user' in entry else ''
                new_hba_entry.address = entry[
                    'address'] if 'address' in entry else ''
                new_hba_entry.auth_method = entry[
                    'auth_method'] if 'auth_method' in entry else ''
                new_hba_entry.auth_options = entry[
                    'auth_options'] if 'auth_options' in entry else ''
            except Exception as e:
                logger.error(e.message)
                raise HTTPError(406, "Invalid HBA entry.")
            new_hba_entry.lazy_check()
        hba_entries.append(new_hba_entry)
    return HBAManager.save_entries(hba_file, hba_entries, new_version)
示例#8
0
def get_hba_raw(conn, config, http_context):
    version = None

    if http_context and 'version' in http_context['query']:
        # Check parameter 'version'
        validate_parameters(http_context['query'],
                            [('version', T_FILE_VERSION, True)])
        version = http_context['query']['version'][0]

    ret = {'filepath': None, 'version': version, 'content': ''}
    hba_file = get_setting(conn, 'hba_file')
    ret['filepath'] = hba_file
    ret['content'] = HBAManager.get_file_content(hba_file, version)
    return ret
示例#9
0
def get_hba(conn, config, http_context):
    version = None
    if http_context and 'version' in http_context['query']:
        # Check parameter 'version'
        validate_parameters(http_context['query'],
                            [('version', T_FILE_VERSION, True)])
        version = http_context['query']['version'][0]

    ret = {'filepath': None, 'version': version, 'entries': []}
    hba_file = get_setting(conn, 'hba_file')
    ret['filepath'] = hba_file
    for hba_entry in HBAManager.get_entries(hba_file, version):
        ret['entries'].append(hba_entry.__dict__)
    return ret
示例#10
0
def post_vacuum(app, post, dbname, schema=None, table=None):
    # Parameters format validation
    if 'datetime' in post:
        validate_parameters(post, [
            ('datetime', T_TIMESTAMP_UTC, False),
        ])
    dt = post.get('datetime', datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"))
    if 'mode' in post:
        validate_parameters(post, [
            ('mode', T_VACUUM_MODE, False),
        ])
    mode = post.get('mode', '')

    with functions.get_postgres(app.config, dbname).connect() as conn:
        return functions.schedule_vacuum(conn, dbname, mode, dt, app,
                                         schema=schema, table=table)
示例#11
0
def post_hba_raw(conn, config, http_context):
    new_version = False

    if 'content' not in http_context['post']:
        raise HTTPError(406, "Parameter 'content' not sent.")
    if http_context and 'new_version' in http_context['post']:
        # Check parameter 'version'
        validate_parameters(http_context['post'],
                            [('new_version', T_NEW_VERSION, False)])
        if http_context['post']['new_version'] is True:
            new_version = True

    hba_file = get_setting(conn, 'hba_file')
    return HBAManager.save_file_content(hba_file,
                                        http_context['post']['content'],
                                        new_version)
示例#12
0
def get_monitoring(http_context, app):
    """Monitoring root API aims to query metrics history.
    Data are sorted by collect timestamp, in ascending order. By default, only
    the most fresh record set is returned. The query parameter 'start' can be
    used as lower bound and must be expressed as a UTC timestamp formatted
    using ISO8601 norm with a terminal 'Z' character. To limit the number of
    returned records to N, the query parameter 'limit' can be used and set to
    N. 'limit' default value is 50, meaning that the maximum number of record
    set this API returns by default is 50.
    """

    # Default values
    start_timestamp = None
    limit = 50

    if 'start' in http_context['query']:
        # Validate start parameter
        validate_parameters(http_context['query'], [
            ('start', T_TIMESTAMP_UTC, True),
        ])
        # Convert it to epoch
        try:
            start_timestamp = (
                datetime.strptime(
                    http_context['query']['start'][0], "%Y-%m-%dT%H:%M:%SZ"
                ) - datetime(1970, 1, 1)
            ).total_seconds()
        except ValueError:
            raise TemboardHTTPError(406, "Invalid timestamp")

    if 'limit' in http_context['query']:
        # Validate limit parameter
        validate_parameters(http_context['query'], [
            ('limit', T_LIMIT, True),
        ])
        limit = int(http_context['query']['limit'][0])

    return [
        json.loads(metric[1]) for metric in db.get_metrics(
            app.config.temboard.home,
            'monitoring.db',
            start_timestamp=start_timestamp,
            limit=limit
        )
    ]
def say_hello_something2(config, http_context):
    """
    "Hello <something>" using GET variable.

    Usage:
    $ export XSESSION=`curl -s -k -X POST --data '{"username":"******", "password":"******"}' https://localhost:2345/login | sed -E "s/^.+\"([a-f0-9]+)\".+$/\1/"`
    $ curl -s -k -H "X-Session:$XSESSION" "https://localhost:2345/hello2/say?something=toto" | python -m json.tool
    {
        "content": "Hello toto"
    }
    """  # noqa
    if http_context and 'something' in http_context['query']:
        validate_parameters(http_context['query'],
                            [('something', T_SOMETHING, True)])
        something = http_context['query']['something'][0]
        return {"content": "Hello %s" % (something)}
    else:
        raise HTTPError(444, "Parameter 'something' not sent.")
示例#14
0
def post_activity_kill(conn, config, http_context):
    validate_parameters(http_context['post'], [('pids', T_PID, True)])
    ret = {'backends': []}
    for pid in http_context['post']['pids']:
        conn.execute("SELECT pg_terminate_backend(%s) AS killed" % (pid))
        # Push a notification.
        try:
            NotificationMgmt.push(
                config,
                Notification(username=http_context['username'],
                             message="Backend %s terminated" % (pid)))
        except (NotificationError, Exception) as e:
            pass

        ret['backends'].append({
            'pid': pid,
            'killed': list(conn.get_rows())[0]['killed']
        })
    return ret
示例#15
0
def delete_hba_version(conn, config, http_context):
    version = None

    if http_context and 'version' in http_context['query']:
        # Check parameter 'version'
        validate_parameters(http_context['query'],
                            [('version', T_FILE_VERSION, True)])
        version = http_context['query']['version'][0]
    if version is None:
        raise HTTPError(406, "HBA version number must be specified.")

    hba_file = get_setting(conn, 'hba_file')
    # Push a notification.
    try:
        NotificationMgmt.push(
            config,
            Notification(username=http_context['username'],
                         message="HBA file version '%s' removed." % (version)))
    except NotificationError as e:
        logger.error(e.message)

    return HBAManager.remove_version(hba_file, version)
示例#16
0
def login(http_context, app, sessions):
    post = http_context['post']
    # Add an unconditional sleeping time to reduce brute-force risks
    time.sleep(1)

    logger.info("Authenticating user: %s" % (post['username']))
    try:
        validate_parameters(post,
                            [('username', T_USERNAME, False),
                             ('password', T_PASSWORD, False)])
        auth_user(app.config.temboard['users'],
                  post['username'], post['password'])
    except HTTPError as e:
        logger.info("Authentication failed.")
        raise e
    try:
        session = sessions.get_by_username(post['username'])
        if not session:
            sessionid = gen_sessionid(post['username'])
            session = Session(sessionid.encode('utf-8'),
                              time.time(),
                              post['username'].encode('utf-8'))
            sessions.add(session)
        else:
            sessionid = session.sessionid
            session.time = time.time()
            sessions.update(session)
        try:
            NotificationMgmt.push(app.config,
                                  Notification(username=post['username'],
                                               message="Login"))
        except NotificationError as e:
            logger.exception(e)

        return {'session': sessionid}
    except (SharedItem_exists, SharedItem_no_free_slot_left) as e:
        logger.exception(e)
        raise HTTPError(500, "Internal error.")
示例#17
0
def post_activity_kill(conn, config, http_context):
    """
    Kill (using pg_terminate_backend()) processes based on a given backend PID
    list.
    """
    validate_parameters(http_context['post'], [('pids', T_PID, True)])
    ret = {'backends': []}
    for pid in http_context['post']['pids']:
        killed = conn.query_scalar(
            "SELECT pg_terminate_backend(%s) AS killed" % (pid))
        # Push a notification.
        try:
            NotificationMgmt.push(
                config,
                Notification(username=http_context['username'],
                             message="Backend %s terminated" % (pid)))
        except (NotificationError, Exception):
            pass

        ret['backends'].append({
            'pid': pid,
            'killed': killed,
        })
    return ret
def get_hello2_say_query(http_context, config):
    """
    "Hello <something>" using GET variable.

    Usage:
    $ export XSESSION=`curl -s -k -X POST --data '{"username":"******", "password":"******"}' https://localhost:2345/login | sed -E "s/^.+\"([a-f0-9]+)\".+$/\1/"`
    $ curl -s -k -H "X-Session:$XSESSION" "https://localhost:2345/hello2/say?something=toto" | python -m json.tool
    {
        "content": "Hello toto"
    }
    """  # noqa
    if http_context and 'something' in http_context['query']:
        validate_parameters(http_context['query'], [
            ('something', T_SOMETHING, True)
        ])
示例#19
0
def get_settings(conn, config, http_context=None):
    # get configuration from files
    files_conf = get_files_configuration(conn)
    # get auto config
    data_directory = get_setting(conn, 'data_directory')
    autoconfig_file = os.path.join(data_directory, 'postgresql.auto.conf')
    auto_conf = parse_configuration_file(autoconfig_file, {})

    filter_query = ''
    if http_context and 'filter' in http_context['query']:
        # Check 'filter' parameters.
        validate_parameters(http_context['query'],
                            [('filter', T_PGSETTINGS_FILTER, True)])
        filter_query = " WHERE name ILIKE '%{0}%'" \
                       " OR short_desc ILIKE '%{0}%'" \
                       " OR extra_desc ILIKE '%{0}%'".format(
                               http_context['query']['filter'][0])
    query = """
SELECT
    name, setting, current_setting(name) AS current_setting,
    CASE WHEN name IN ('max_wal_size', 'min_wal_size')
    THEN current_setting('wal_segment_size') ELSE unit END AS unit,
    vartype, min_val, max_val, enumvals, context, category,
    short_desc||' '||coalesce(extra_desc, '') AS desc, boot_val
FROM pg_settings
%s ORDER BY category, name
    """ % (filter_query)
    conn.execute(query)
    ret = []
    do_not_format_names = ['unix_socket_permissions']
    for row in conn.get_rows():
        if http_context and len(http_context['urlvars']) > 0:
            if http_context['urlvars'][0] != row['category']:
                continue
        cat_exists = False
        i = 0
        for ret_cat in ret:
            if ret_cat['category'] == row['category']:
                cat_exists = True
                break
            i += 1
        row_dict = {
            'name': row['name'],
            'setting': row['setting'],
            'setting_raw': row['current_setting'],
            'unit': row['unit'],
            'vartype': row['vartype'],
            'min_val': row['min_val'],
            'max_val': row['max_val'],
            'boot_val': row['boot_val'],
            'auto_val': None,
            'auto_val_raw': None,
            'file_val': None,
            'file_val_raw': None,
            'enumvals': row['enumvals'],
            'context': row['context'],
            'desc': row['desc']
        }

        name = row['name']

        if name in auto_conf:
            setting = preformat(auto_conf[name].setting, row['vartype'])
            row_dict['auto_val'] = setting
            row_dict['auto_val_raw'] = setting

        if name in files_conf:
            setting = preformat(files_conf[name].setting, row['vartype'])
            row_dict['file_val'] = setting
            row_dict['file_val_raw'] = setting

        if name not in do_not_format_names:
            for e in [
                    'setting', 'min_val', 'max_val', 'boot_val', 'auto_val',
                    'file_val'
            ]:
                row_dict[e] = format_setting(row_dict[e], row['vartype'],
                                             row_dict['unit'])

        if not cat_exists:
            ret.append({'category': row['category'], 'rows': [row_dict]})
        else:
            ret[i]['rows'].append(row_dict)

    return ret
示例#20
0
def get_settings(conn, config, http_context=None):
    auto_conf = get_auto_configuration(conn)
    file_conf = get_file_configuration(conn)
    filter_query = ''
    if http_context and 'filter' in http_context['query']:
        # Check 'filter' parameters.
        validate_parameters(http_context['query'],
                            [('filter', T_PGSETTINGS_FILTER, True)])
        filter_query = " WHERE name ILIKE '%%%s%%'" % (
            http_context['query']['filter'][0], )
    query = """
SELECT
    name,
    setting,
    current_setting(name) AS current_setting,
    CASE WHEN name IN ('max_wal_size', 'min_wal_size') THEN current_setting('wal_segment_size') ELSE unit END AS unit,
    vartype,
    min_val,
    max_val,
    enumvals,
    context,
    category,
    short_desc||' '||coalesce(extra_desc, '') AS desc,
    boot_val
FROM pg_settings
%s ORDER BY category, name
    """ % (filter_query)
    conn.execute(query)
    ret = []
    do_not_format_names = ['unix_socket_permissions']
    for row in conn.get_rows():
        if http_context and len(http_context['urlvars']) > 0:
            if http_context['urlvars'][0] != row['category']:
                continue
        cat_exists = False
        i = 0
        for ret_cat in ret:
            if ret_cat['category'] == row['category']:
                cat_exists = True
                break
            i += 1
        prev_cat = row['category']
        in_auto_conf = False
        if row['name'] in auto_conf:
            in_auto_conf = True
        in_file_conf = False
        if row['name'] in file_conf:
            in_file_conf = True
        row_dict = {
            'name': row['name'],
            'setting': row['setting'],
            'setting_raw': row['current_setting'],
            'unit': row['unit'],
            'vartype': row['vartype'],
            'min_val': row['min_val'],
            'max_val': row['max_val'],
            'boot_val': row['boot_val'],
            'auto_val': None,
            'auto_val_raw': None,
            'file_val': None,
            'file_val_raw': None,
            'enumvals': row['enumvals'],
            'context': row['context'],
            'desc': row['desc']
        }

        if in_auto_conf:
            val = auto_conf[row['name']]
            if val.startswith("'") and val.endswith("'"):
                val = val[1:-1]
            if row_dict['vartype'] == u'bool':
                if val == 'true':
                    val = 'on'
                elif val == 'false':
                    val = 'off'
            row_dict['auto_val'] = val
            row_dict['auto_val_raw'] = val

        if in_file_conf:
            val = file_conf[row['name']]
            if val.startswith("'") and val.endswith("'"):
                val = val[1:-1]
            if row_dict['vartype'] == u'bool':
                if val == 'true':
                    val = 'on'
                elif val == 'false':
                    val = 'off'
            row_dict['file_val'] = val
            row_dict['file_val_raw'] = val

        if row['name'] not in do_not_format_names and row[
                'vartype'] == u'integer':
            row_dict['setting'] = int(row_dict['setting'])
            row_dict['min_val'] = int(row_dict['min_val'])
            row_dict['max_val'] = int(row_dict['max_val'])
            row_dict['boot_val'] = int(
                human_to_number(row_dict['boot_val'], row_dict['unit']))
            if in_auto_conf:
                row_dict['auto_val'] = int(
                    human_to_number(row_dict['auto_val'], row_dict['unit']))
            if in_file_conf:
                row_dict['file_val'] = int(
                    human_to_number(row_dict['file_val'], row_dict['unit']))
        elif row['name'] not in do_not_format_names and row[
                'vartype'] == u'real':
            row_dict['setting'] = float(row_dict['setting'])
            row_dict['min_val'] = float(row_dict['min_val'])
            row_dict['max_val'] = float(row_dict['max_val'])
            row_dict['boot_val'] = float(row_dict['boot_val'])
            if in_auto_conf:
                row_dict['auto_val'] = float(row_dict['auto_val'])
            if in_file_conf:
                row_dict['file_val'] = float(row_dict['file_val'])

        if not cat_exists:
            ret.append({'category': row['category'], 'rows': [row_dict]})
        else:
            ret[i]['rows'].append(row_dict)
    return ret
示例#21
0
def post_settings(conn, config, http_context):
    set_logger_name("settings")
    logger = get_logger(config)

    if http_context and 'filter' in http_context['query']:
        # Check 'filter' parameters.
        validate_parameters(http_context['query'],
                            [('filter', T_PGSETTINGS_FILTER, True)])
    pg_config_categories = get_settings(conn, config, None)
    if 'settings' not in http_context['post']:
        raise HTTPError(406, "Parameter 'settings' not sent.")
    settings = http_context['post']['settings']
    ret = {'settings': []}
    do_not_check_names = ['unix_socket_permissions', 'log_file_mode']
    logger.debug(settings)
    for setting in settings:
        if 'name' not in setting \
            or 'setting' not in setting:
            raise HTTPError(406, "setting item malformed.")
        checked = False
        try:
            for pg_config_category in pg_config_categories:
                for pg_config_item in pg_config_category['rows']:
                    if pg_config_item['name'] == setting['name']:
                        if pg_config_item['name'] in do_not_check_names:
                            checked = True
                            raise Exception()
                        if pg_config_item['vartype'] == u'integer':
                            # Integers handling.
                            if pg_config_item['min_val'] and pg_config_item['unit'] and \
                                (int(human_to_number(setting['setting'], pg_config_item['unit'])) < int(pg_config_item['min_val'])):
                                raise HTTPError(
                                    406, "%s: Invalid setting." %
                                    (pg_config_item['name']))
                            if pg_config_item['max_val'] and pg_config_item['unit'] and \
                                (int(human_to_number(setting['setting'], pg_config_item['unit'])) > int(pg_config_item['max_val'])):
                                raise HTTPError(
                                    406, "%s: Invalid setting." %
                                    (pg_config_item['name']))
                            setting['setting'] = pg_escape(setting['setting'])
                            if ((setting['setting'].startswith("'") and setting['setting'].endswith("'")) or \
                                (setting['setting'].startswith('"') and setting['setting'].endswith('"'))):
                                setting['setting'] = setting['setting'][1:-1]
                            if setting['setting'] == '':
                                setting['setting'] = None
                            checked = True
                        if pg_config_item['vartype'] == u'real':
                            # Real handling.
                            if pg_config_item['min_val'] and \
                                (float(setting['setting']) < float(pg_config_item['min_val'])):
                                raise HTTPError(
                                    406, "%s: Invalid setting." %
                                    (pg_config_item['name']))
                            if pg_config_item['max_val'] and \
                                (float(setting['setting']) > float(pg_config_item['max_val'])):
                                raise HTTPError(
                                    406, "%s: Invalid setting." %
                                    (pg_config_item['name']))
                            setting['setting'] = float(setting['setting'])
                            checked = True
                        if pg_config_item['vartype'] == u'bool':
                            # Boolean handling.
                            if setting['setting'].lower() not in [
                                    u'on', u'off'
                            ]:
                                raise HTTPError(
                                    406, 'Invalid setting: %s.' %
                                    (setting['setting'].lower()))
                            checked = True
                        if pg_config_item['vartype'] == u'enum':
                            # Enum handling.
                            if len(pg_config_item['enumvals']) > 0:
                                enumvals = [
                                    re.sub(r"^[\"\'](.+)[\"\ ']$", r"\1",
                                           enumval) for enumval in
                                    pg_config_item['enumvals'][1:-1].split(',')
                                ]
                                if ((setting['setting'].startswith("'") and setting['setting'].endswith("'")) or \
                                    (setting['setting'].startswith('"') and setting['setting'].endswith('"'))):
                                    setting['setting'] = setting['setting'][
                                        1:-1]
                                if setting['setting'] not in enumvals:
                                    raise HTTPError(
                                        406, 'Invalid setting: %s.' %
                                        (setting['setting']))
                                checked = True
                        if pg_config_item['vartype'] == u'string':
                            # String handling.
                            # setting must be escaped.
                            setting['setting'] = pg_escape(
                                str(setting['setting']))
                            if ((setting['setting'].startswith("'") and setting['setting'].endswith("'")) or \
                                (setting['setting'].startswith('"') and setting['setting'].endswith('"'))):
                                setting['setting'] = setting['setting'][1:-1]
                            if setting['setting'] == '':
                                setting['setting'] = None
                            checked = True
                        raise Exception()
        except HTTPError as e:
            raise HTTPError(e.code, e.message['error'])
        except Exception as e:
            pass
        if not checked:
            raise HTTPError(
                406, 'Parameter %s can\'t be checked.' % (setting['name']))
        if 'force' not in setting:
            setting['force'] = 'false'
        if ((pg_config_item['vartype'] == u'integer' and setting['setting'] != pg_config_item['setting_raw']) or \
            (pg_config_item['vartype'] == u'real' and float(setting['setting']) != float(pg_config_item['setting'])) or \
            (pg_config_item['vartype'] not in [ u'integer', u'real' ] and setting['setting'] != pg_config_item['setting'])) or \
            (setting['force'] == 'true'):
            # At this point, all incoming parameters have been checked.
            if setting['setting']:
                query = "ALTER SYSTEM SET %s TO '%s'" % (setting['name'],
                                                         setting['setting'])
            else:
                query = "ALTER SYSTEM RESET %s;" % (setting['name'])

            logger.debug(query)

            # Push a notification on setting change.
            try:
                NotificationMgmt.push(
                    config,
                    Notification(
                        username=http_context['username'],
                        message="Setting '%s' changed: '%s' -> '%s'" %
                        (pg_config_item['name'], pg_config_item['setting_raw'],
                         setting['setting'])))
            except NotificationError as e:
                logger.error(e.message)

            try:
                conn.execute(query)
            except error as e:
                raise HTTPError(408, "%s: %s" % (setting['name'], e.message))
            ret['settings'].append({
                'name':
                pg_config_item['name'],
                'setting':
                setting['setting'],
                'previous_setting':
                pg_config_item['setting_raw'],
                'restart':
                True if pg_config_item['context']
                in ['internal', 'postmaster'] else False
            })
    # Reload PG configuration.
    conn.execute("SELECT pg_reload_conf()")
    # Push a notification.
    try:
        NotificationMgmt.push(
            config,
            Notification(username=http_context['username'],
                         message="PostgreSQL reload"))
    except NotificationError as e:
        logger.error(e.message)

    return ret
示例#22
0
def login(http_context,
          queue_in=None,
          config=None,
          sessions=None,
          commands=None):
    """
    @api {get} /login User login
    @apiVersion 0.0.1
    @apiName UserLogin
    @apiGroup User

    @apiParam {String} username Username.
    @apiParam {String} password Password.

    @apiSuccess {String} sessions Session ID.

    @apiExample {curl} Example usage:
        curl -k -X POST -H "Content-Type: application/json" -d '{"username": "******", "password": "******"}' \
            https://localhost:2345/login

    @apiSuccessExample Success-Reponse:
        HTTP/1.0 200 OK
        Server: temboard-agent/0.0.1 Python/2.7.8
        Date: Wed, 22 Apr 2015 12:19:48 GMT
        Content-type: application/json

        {"session": "fa452548403ac53f2158a65f5eb6db9723d2b07238dd83f5b6d9ca52ce817b63"}

    @apiError (500 error) error Internal error.
    @apiError (404 error) error Invalid username or password.
    @apiError (406 error) error Username or password malformed or missing.

    @apiErrorExample 404 error example
        HTTP/1.0 404 Not Found
        Server: temboard-agent/0.0.1 Python/2.7.8
        Date: Wed, 22 Apr 2015 12:20:33 GMT
        Content-type: application/json

        {"error": "Invalid username/password."}

    @apiErrorExample 406 error example
        HTTP/1.0 406 Not Acceptable
        Server: temboard-agent/0.0.1 Python/2.7.8
        Date: Wed, 22 Apr 2015 12:21:01 GMT
        Content-type: application/json

        {"error": "Parameter 'password' is malformed."}
    """
    post = http_context['post']
    set_logger_name("api")
    logger = get_logger(config)
    # Add an unconditional sleeping time to reduce brute-force risks
    time.sleep(1)

    logger.info("Authenticating user: %s" % (post['username']))
    try:
        validate_parameters(post, [('username', T_USERNAME, False),
                                   ('password', T_PASSWORD, False)])
        auth_user(config.temboard['users'], post['username'], post['password'])
    except HTTPError as e:
        logger.traceback(get_tb())
        logger.error(e.message)
        logger.info("Authentication failed.")
        raise e
    try:
        session = sessions.get_by_username(post['username'])
        if not session:
            sessionid = gen_sessionid(post['username'])
            session = Session(sessionid.encode('utf-8'), time.time(),
                              post['username'].encode('utf-8'))
            sessions.add(session)
        else:
            sessionid = session.sessionid
            session.time = time.time()
            sessions.update(session)
        try:
            NotificationMgmt.push(
                config, Notification(username=post['username'],
                                     message="Login"))
        except NotificationError as e:
            logger.traceback(get_tb())
            logger.error(e.message)

    except (SharedItem_exists, SharedItem_no_free_slot_left) as e:
        logger.traceback(get_tb())
        logger.error(e.message)
        raise HTTPError(500, "Internal error.")
    return {'session': sessionid}
示例#23
0
def post_pg_control(http_context, config=None, sessions=None):
    # NOTE: in this case we don't want to use api functions wrapper, it leads
    # to "Broken pipe" error with debian init.d script on start/restart.
    # This is probably due to getattr() call.
    post = http_context['post']

    try:
        check_sessionid(http_context['headers'], sessions)
        # Check POST parameters.
        validate_parameters(post, [('action', T_CONTROL, False)])
        session = sessions.get_by_sessionid(
            http_context['headers']['X-Session'].encode('utf-8'))
    except (Exception, HTTPError) as e:
        logger.exception(str(e))
        logger.debug(http_context)
        if isinstance(e, HTTPError):
            raise e
        else:
            raise HTTPError(500, "Internal error.")

    try:
        NotificationMgmt.push(
            config,
            Notification(username=session.username,
                         message="PostgreSQL %s" % post['action']))
    except (NotificationError, Exception) as e:
        logger.exception(str(e))

    try:
        logger.info("PostgreSQL '%s' requested." % (post['action']))
        cmd_args = oneline_cmd_to_array(
            config.plugins['administration']['pg_ctl'] % (post['action']))
        (rcode, stdout, stderr) = exec_script(cmd_args)
        if rcode != 0:
            raise Exception(str(stderr))
        # Let's check if PostgreSQL is up & running after having executed
        # 'start' or 'restart' action.
        if post['action'] in ['start', 'restart']:
            conn = connector(host=config.postgresql['host'],
                             port=config.postgresql['port'],
                             user=config.postgresql['user'],
                             password=config.postgresql['password'],
                             database=config.postgresql['dbname'])
            # When a start/restart operation is requested, after the
            # startup/pg_ctl script has been executed then we check that
            # postgres is up & running:
            # while the PG conn. is not working then, for 10 seconds (max)
            # we'll check (connect/SELECT 1/disconnect) the connection, every
            # 0.5 second.
            retry = True
            t_start = time.time()
            while retry:
                try:
                    conn.connect()
                    conn.execute('SELECT 1')
                    conn.close()
                    logger.info("Done.")
                    return {'action': post['action'], 'state': 'ok'}
                except error:
                    if (time.time() - t_start) > 10:
                        try:
                            conn.close()
                        except error:
                            pass
                        except Exception:
                            pass
                        logger.info("Failed.")
                        return {'action': post['action'], 'state': 'ko'}
                time.sleep(0.5)

        elif post['action'] == 'stop':
            conn = connector(host=config.postgresql['host'],
                             port=config.postgresql['port'],
                             user=config.postgresql['user'],
                             password=config.postgresql['password'],
                             database=config.postgresql['dbname'])
            # Check the PG conn is not working anymore.
            try:
                retry = True
                t_start = time.time()
                while retry:
                    conn.connect()
                    conn.execute('SELECT 1')
                    conn.close()
                    time.sleep(0.5)
                    if (time.time() - t_start) > 10:
                        retry = False
                logger.info("Failed.")
                return {'action': post['action'], 'state': 'ko'}
            except error:
                logger.info("Done.")
                return {'action': post['action'], 'state': 'ok'}
        logger.info("Done.")
        return {'action': post['action'], 'state': 'ok'}
    except (Exception, error, HTTPError) as e:
        logger.exception(str(e))
        logger.info("Failed")
        if isinstance(e, HTTPError):
            raise e
        else:
            raise HTTPError(500, "Internal error.")
示例#24
0
def post_pg_control(http_context, queue_in = None, config = None, sessions = None, commands = None):
    # NOTE: in this case we don't want to use api functions wrapper, it leads
    # to "Broken pipe" error with debian init.d on start/restart. This is
    # probably due to getattr() call.
    set_logger_name("administration")
    # Get a new logger.
    logger = get_logger(config)
    check_sessionid(http_context['headers'], sessions)
    post = http_context['post']
    # Check POST parameters.
    validate_parameters(post, [
        ('action', T_CONTROL, False)
    ])

    try:
        session = sessions.get_by_sessionid(http_context['headers']['X-Session'].encode('utf-8'))
        NotificationMgmt.push(config, Notification(
                                        username = session.username,
                                        message = "PostgreSQL %s" % (post['action'])))
    except NotificationError as e:
        logger.error(e.message)

    cmd_args = oneline_cmd_to_array(config.plugins['administration']['pg_ctl'] % (post['action']))
    (rcode, stdout, stderr) = exec_script(cmd_args)
    if rcode != 0:
        raise HTTPError(408, str(stderr))
    # Let's check if postgresql is up & running on 'start' or 'restart' action.
    if post['action'] in ['start', 'restart']:
        conn = connector(
            host = config.postgresql['host'],
            port = config.postgresql['port'],
            user = config.postgresql['user'],
            password = config.postgresql['password'],
            database = config.postgresql['dbname']
        )
        # When a start/restart operation is requested, after the startup/pg_ctl
        # script is executed we check that postgres is up & running: while the
        # PG connection is not working, during 10 seconds (max) we'll check
        # (connect/SELECT 1/disconnect) the connection, every 0.5 second.
        retry = True
        t_start = time.time()
        while retry:
            try:
                conn.connect()
                conn.execute('SELECT 1')
                conn.close()
                return {'action': post['action'], 'state': 'ok'}
            except error as e:
                if (time.time() - t_start) > 10:
                    try:
                        conn.close()
                    except error as e:
                        pass
                    except Exception:
                        pass
                    return {'action': post['action'], 'state': 'ko'}
            time.sleep(0.5)

    elif post['action'] == 'stop':
        conn = connector(
            host = config.postgresql['host'],
            port = config.postgresql['port'],
            user = config.postgresql['user'],
            password = config.postgresql['password'],
            database = config.postgresql['dbname']
        )
        # Check the PG conn is not working anymore.
        try:
            retry = True
            t_start = time.time()
            while retry:
                conn.connect()
                conn.execute('SELECT 1')
                conn.close()
                time.sleep(0.5)
                if (time.time() - t_start) > 10:
                    retry = False
            return {'action': post['action'], 'state': 'ko'}
        except error as e:
            return {'action': post['action'], 'state': 'ok'}
    return {'action': post['action'], 'state': 'ok'}