843 lines
25 KiB
Python
843 lines
25 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Python client for InfluxDB v0.8."""
|
|
|
|
import warnings
|
|
|
|
import json
|
|
import socket
|
|
import requests
|
|
import requests.exceptions
|
|
from six.moves import xrange
|
|
from six.moves.urllib.parse import urlparse
|
|
|
|
from influxdb import chunked_json
|
|
|
|
session = requests.Session()
|
|
|
|
|
|
class InfluxDBClientError(Exception):
|
|
"""Raised when an error occurs in the request."""
|
|
|
|
def __init__(self, content, code=-1):
|
|
"""Initialize an InfluxDBClientError handler."""
|
|
super(InfluxDBClientError, self).__init__(
|
|
"{0}: {1}".format(code, content))
|
|
self.content = content
|
|
self.code = code
|
|
|
|
|
|
class InfluxDBClient(object):
|
|
"""Define the standard InfluxDBClient for influxdb v0.8.
|
|
|
|
The ``InfluxDBClient`` object holds information necessary to connect
|
|
to InfluxDB. Requests can be made to InfluxDB directly through the client.
|
|
|
|
:param host: hostname to connect to InfluxDB, defaults to 'localhost'
|
|
:type host: string
|
|
:param port: port to connect to InfluxDB, defaults to 'localhost'
|
|
:type port: int
|
|
:param username: user to connect, defaults to 'root'
|
|
:type username: string
|
|
:param password: password of the user, defaults to 'root'
|
|
:type password: string
|
|
:param database: database name to connect to, defaults is None
|
|
:type database: string
|
|
:param ssl: use https instead of http to connect to InfluxDB, defaults is
|
|
False
|
|
:type ssl: boolean
|
|
:param verify_ssl: verify SSL certificates for HTTPS requests, defaults is
|
|
False
|
|
:type verify_ssl: boolean
|
|
:param retries: number of retries your client will try before aborting,
|
|
defaults to 3. 0 indicates try until success
|
|
:type retries: int
|
|
:param timeout: number of seconds Requests will wait for your client to
|
|
establish a connection, defaults to None
|
|
:type timeout: int
|
|
:param use_udp: use UDP to connect to InfluxDB, defaults is False
|
|
:type use_udp: int
|
|
:param udp_port: UDP port to connect to InfluxDB, defaults is 4444
|
|
:type udp_port: int
|
|
"""
|
|
|
|
def __init__(self,
|
|
host='localhost',
|
|
port=8086,
|
|
username='root',
|
|
password='root',
|
|
database=None,
|
|
ssl=False,
|
|
verify_ssl=False,
|
|
timeout=None,
|
|
retries=3,
|
|
use_udp=False,
|
|
udp_port=4444):
|
|
"""Construct a new InfluxDBClient object."""
|
|
self._host = host
|
|
self._port = port
|
|
self._username = username
|
|
self._password = password
|
|
self._database = database
|
|
self._timeout = timeout
|
|
self._retries = retries
|
|
|
|
self._verify_ssl = verify_ssl
|
|
|
|
self._use_udp = use_udp
|
|
self._udp_port = udp_port
|
|
if use_udp:
|
|
self.udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
|
|
self._scheme = "http"
|
|
|
|
if ssl is True:
|
|
self._scheme = "https"
|
|
|
|
self._baseurl = "{0}://{1}:{2}".format(
|
|
self._scheme,
|
|
self._host,
|
|
self._port)
|
|
|
|
self._headers = {
|
|
'Content-type': 'application/json',
|
|
'Accept': 'text/plain'}
|
|
|
|
@staticmethod
|
|
def from_dsn(dsn, **kwargs):
|
|
r"""Return an instaance of InfluxDBClient from given data source name.
|
|
|
|
Returns an instance of InfluxDBClient from the provided data source
|
|
name. Supported schemes are "influxdb", "https+influxdb",
|
|
"udp+influxdb". Parameters for the InfluxDBClient constructor may be
|
|
also be passed to this function.
|
|
|
|
Examples:
|
|
>> cli = InfluxDBClient.from_dsn('influxdb://username:password@\
|
|
... localhost:8086/databasename', timeout=5)
|
|
>> type(cli)
|
|
<class 'influxdb.client.InfluxDBClient'>
|
|
>> cli = InfluxDBClient.from_dsn('udp+influxdb://username:pass@\
|
|
... localhost:8086/databasename', timeout=5, udp_port=159)
|
|
>> print('{0._baseurl} - {0.use_udp} {0.udp_port}'.format(cli))
|
|
http://localhost:8086 - True 159
|
|
|
|
:param dsn: data source name
|
|
:type dsn: string
|
|
:param **kwargs: additional parameters for InfluxDBClient.
|
|
:type **kwargs: dict
|
|
:note: parameters provided in **kwargs may override dsn parameters.
|
|
:note: when using "udp+influxdb" the specified port (if any) will be
|
|
used for the TCP connection; specify the udp port with the additional
|
|
udp_port parameter (cf. examples).
|
|
:raise ValueError: if the provided DSN has any unexpected value.
|
|
|
|
"""
|
|
init_args = {}
|
|
conn_params = urlparse(dsn)
|
|
scheme_info = conn_params.scheme.split('+')
|
|
|
|
if len(scheme_info) == 1:
|
|
scheme = scheme_info[0]
|
|
modifier = None
|
|
else:
|
|
modifier, scheme = scheme_info
|
|
|
|
if scheme != 'influxdb':
|
|
raise ValueError('Unknown scheme "{0}".'.format(scheme))
|
|
|
|
if modifier:
|
|
if modifier == 'udp':
|
|
init_args['use_udp'] = True
|
|
elif modifier == 'https':
|
|
init_args['ssl'] = True
|
|
else:
|
|
raise ValueError('Unknown modifier "{0}".'.format(modifier))
|
|
|
|
if conn_params.hostname:
|
|
init_args['host'] = conn_params.hostname
|
|
if conn_params.port:
|
|
init_args['port'] = conn_params.port
|
|
if conn_params.username:
|
|
init_args['username'] = conn_params.username
|
|
if conn_params.password:
|
|
init_args['password'] = conn_params.password
|
|
if conn_params.path and len(conn_params.path) > 1:
|
|
init_args['database'] = conn_params.path[1:]
|
|
|
|
init_args.update(kwargs)
|
|
|
|
return InfluxDBClient(**init_args)
|
|
|
|
# Change member variables
|
|
|
|
def switch_database(self, database):
|
|
"""Change client database.
|
|
|
|
:param database: the new database name to switch to
|
|
:type database: string
|
|
"""
|
|
self._database = database
|
|
|
|
def switch_db(self, database):
|
|
"""Change client database.
|
|
|
|
DEPRECATED.
|
|
"""
|
|
warnings.warn(
|
|
"switch_db is deprecated, and will be removed "
|
|
"in future versions. Please use "
|
|
"``InfluxDBClient.switch_database(database)`` instead.",
|
|
FutureWarning)
|
|
return self.switch_database(database)
|
|
|
|
def switch_user(self, username, password):
|
|
"""Change client username.
|
|
|
|
:param username: the new username to switch to
|
|
:type username: string
|
|
:param password: the new password to switch to
|
|
:type password: string
|
|
"""
|
|
self._username = username
|
|
self._password = password
|
|
|
|
def request(self, url, method='GET', params=None, data=None,
|
|
expected_response_code=200):
|
|
"""Make a http request to API."""
|
|
url = "{0}/{1}".format(self._baseurl, url)
|
|
|
|
if params is None:
|
|
params = {}
|
|
|
|
auth = {
|
|
'u': self._username,
|
|
'p': self._password
|
|
}
|
|
|
|
params.update(auth)
|
|
|
|
if data is not None and not isinstance(data, str):
|
|
data = json.dumps(data)
|
|
|
|
retry = True
|
|
_try = 0
|
|
# Try to send the request more than once by default (see #103)
|
|
while retry:
|
|
try:
|
|
response = session.request(
|
|
method=method,
|
|
url=url,
|
|
params=params,
|
|
data=data,
|
|
headers=self._headers,
|
|
verify=self._verify_ssl,
|
|
timeout=self._timeout
|
|
)
|
|
break
|
|
except (requests.exceptions.ConnectionError,
|
|
requests.exceptions.Timeout):
|
|
_try += 1
|
|
if self._retries != 0:
|
|
retry = _try < self._retries
|
|
else:
|
|
raise requests.exceptions.ConnectionError
|
|
|
|
if response.status_code == expected_response_code:
|
|
return response
|
|
else:
|
|
raise InfluxDBClientError(response.content, response.status_code)
|
|
|
|
def write(self, data):
|
|
"""Provide as convenience for influxdb v0.9.0, this may change."""
|
|
self.request(
|
|
url="write",
|
|
method='POST',
|
|
params=None,
|
|
data=data,
|
|
expected_response_code=200
|
|
)
|
|
return True
|
|
|
|
# Writing Data
|
|
#
|
|
# Assuming you have a database named foo_production you can write data
|
|
# by doing a POST to /db/foo_production/series?u=some_user&p=some_password
|
|
# with a JSON body of points.
|
|
|
|
def write_points(self, data, time_precision='s', *args, **kwargs):
|
|
"""Write to multiple time series names.
|
|
|
|
An example data blob is:
|
|
|
|
data = [
|
|
{
|
|
"points": [
|
|
[
|
|
12
|
|
]
|
|
],
|
|
"name": "cpu_load_short",
|
|
"columns": [
|
|
"value"
|
|
]
|
|
}
|
|
]
|
|
|
|
:param data: A list of dicts in InfluxDB 0.8.x data format.
|
|
:param time_precision: [Optional, default 's'] Either 's', 'm', 'ms'
|
|
or 'u'.
|
|
:param batch_size: [Optional] Value to write the points in batches
|
|
instead of all at one time. Useful for when doing data dumps from
|
|
one database to another or when doing a massive write operation
|
|
:type batch_size: int
|
|
|
|
"""
|
|
def list_chunks(l, n):
|
|
"""Yield successive n-sized chunks from l."""
|
|
for i in xrange(0, len(l), n):
|
|
yield l[i:i + n]
|
|
|
|
batch_size = kwargs.get('batch_size')
|
|
if batch_size and batch_size > 0:
|
|
for item in data:
|
|
name = item.get('name')
|
|
columns = item.get('columns')
|
|
point_list = item.get('points', [])
|
|
|
|
for batch in list_chunks(point_list, batch_size):
|
|
item = [{
|
|
"points": batch,
|
|
"name": name,
|
|
"columns": columns
|
|
}]
|
|
self._write_points(
|
|
data=item,
|
|
time_precision=time_precision)
|
|
return True
|
|
|
|
return self._write_points(data=data,
|
|
time_precision=time_precision)
|
|
|
|
def write_points_with_precision(self, data, time_precision='s'):
|
|
"""Write to multiple time series names.
|
|
|
|
DEPRECATED.
|
|
"""
|
|
warnings.warn(
|
|
"write_points_with_precision is deprecated, and will be removed "
|
|
"in future versions. Please use "
|
|
"``InfluxDBClient.write_points(time_precision='..')`` instead.",
|
|
FutureWarning)
|
|
return self._write_points(data=data, time_precision=time_precision)
|
|
|
|
def _write_points(self, data, time_precision):
|
|
if time_precision not in ['s', 'm', 'ms', 'u']:
|
|
raise Exception(
|
|
"Invalid time precision is given. (use 's', 'm', 'ms' or 'u')")
|
|
|
|
if self._use_udp and time_precision != 's':
|
|
raise Exception(
|
|
"InfluxDB only supports seconds precision for udp writes"
|
|
)
|
|
|
|
url = "db/{0}/series".format(self._database)
|
|
|
|
params = {
|
|
'time_precision': time_precision
|
|
}
|
|
|
|
if self._use_udp:
|
|
self.send_packet(data)
|
|
else:
|
|
self.request(
|
|
url=url,
|
|
method='POST',
|
|
params=params,
|
|
data=data,
|
|
expected_response_code=200
|
|
)
|
|
|
|
return True
|
|
|
|
# One Time Deletes
|
|
|
|
def delete_points(self, name):
|
|
"""Delete an entire series."""
|
|
url = "db/{0}/series/{1}".format(self._database, name)
|
|
|
|
self.request(
|
|
url=url,
|
|
method='DELETE',
|
|
expected_response_code=204
|
|
)
|
|
|
|
return True
|
|
|
|
# Regularly Scheduled Deletes
|
|
|
|
def create_scheduled_delete(self, json_body):
|
|
"""Create schedule delete from database.
|
|
|
|
2013-11-08: This endpoint has not been implemented yet in ver0.0.8,
|
|
but it is documented in http://influxdb.org/docs/api/http.html.
|
|
See also: src/api/http/api.go:l57
|
|
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
# get list of deletes
|
|
# curl http://localhost:8086/db/site_dev/scheduled_deletes
|
|
#
|
|
# remove a regularly scheduled delete
|
|
# curl -X DELETE http://localhost:8086/db/site_dev/scheduled_deletes/:id
|
|
|
|
def get_list_scheduled_delete(self):
|
|
"""Get list of scheduled deletes.
|
|
|
|
2013-11-08: This endpoint has not been implemented yet in ver0.0.8,
|
|
but it is documented in http://influxdb.org/docs/api/http.html.
|
|
See also: src/api/http/api.go:l57
|
|
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def remove_scheduled_delete(self, delete_id):
|
|
"""Remove scheduled delete.
|
|
|
|
2013-11-08: This endpoint has not been implemented yet in ver0.0.8,
|
|
but it is documented in http://influxdb.org/docs/api/http.html.
|
|
See also: src/api/http/api.go:l57
|
|
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def query(self, query, time_precision='s', chunked=False):
|
|
"""Query data from the influxdb v0.8 database.
|
|
|
|
:param time_precision: [Optional, default 's'] Either 's', 'm', 'ms'
|
|
or 'u'.
|
|
:param chunked: [Optional, default=False] True if the data shall be
|
|
retrieved in chunks, False otherwise.
|
|
"""
|
|
return self._query(query, time_precision=time_precision,
|
|
chunked=chunked)
|
|
|
|
# Querying Data
|
|
#
|
|
# GET db/:name/series. It takes five parameters
|
|
def _query(self, query, time_precision='s', chunked=False):
|
|
if time_precision not in ['s', 'm', 'ms', 'u']:
|
|
raise Exception(
|
|
"Invalid time precision is given. (use 's', 'm', 'ms' or 'u')")
|
|
|
|
if chunked is True:
|
|
chunked_param = 'true'
|
|
else:
|
|
chunked_param = 'false'
|
|
|
|
# Build the URL of the series to query
|
|
url = "db/{0}/series".format(self._database)
|
|
|
|
params = {
|
|
'q': query,
|
|
'time_precision': time_precision,
|
|
'chunked': chunked_param
|
|
}
|
|
|
|
response = self.request(
|
|
url=url,
|
|
method='GET',
|
|
params=params,
|
|
expected_response_code=200
|
|
)
|
|
|
|
if chunked:
|
|
try:
|
|
decoded = chunked_json.loads(response.content.decode())
|
|
except UnicodeDecodeError:
|
|
decoded = chunked_json.loads(response.content.decode('utf-8'))
|
|
|
|
return list(decoded)
|
|
|
|
return response.json()
|
|
|
|
# Creating and Dropping Databases
|
|
#
|
|
# ### create a database
|
|
# curl -X POST http://localhost:8086/db -d '{"name": "site_development"}'
|
|
#
|
|
# ### drop a database
|
|
# curl -X DELETE http://localhost:8086/db/site_development
|
|
|
|
def create_database(self, database):
|
|
"""Create a database on the InfluxDB server.
|
|
|
|
:param database: the name of the database to create
|
|
:type database: string
|
|
:rtype: boolean
|
|
"""
|
|
url = "db"
|
|
|
|
data = {'name': database}
|
|
|
|
self.request(
|
|
url=url,
|
|
method='POST',
|
|
data=data,
|
|
expected_response_code=201
|
|
)
|
|
|
|
return True
|
|
|
|
def delete_database(self, database):
|
|
"""Drop a database on the InfluxDB server.
|
|
|
|
:param database: the name of the database to delete
|
|
:type database: string
|
|
:rtype: boolean
|
|
"""
|
|
url = "db/{0}".format(database)
|
|
|
|
self.request(
|
|
url=url,
|
|
method='DELETE',
|
|
expected_response_code=204
|
|
)
|
|
|
|
return True
|
|
|
|
# ### get list of databases
|
|
# curl -X GET http://localhost:8086/db
|
|
|
|
def get_list_database(self):
|
|
"""Get the list of databases."""
|
|
url = "db"
|
|
|
|
response = self.request(
|
|
url=url,
|
|
method='GET',
|
|
expected_response_code=200
|
|
)
|
|
|
|
return response.json()
|
|
|
|
def get_database_list(self):
|
|
"""Get the list of databases.
|
|
|
|
DEPRECATED.
|
|
"""
|
|
warnings.warn(
|
|
"get_database_list is deprecated, and will be removed "
|
|
"in future versions. Please use "
|
|
"``InfluxDBClient.get_list_database`` instead.",
|
|
FutureWarning)
|
|
return self.get_list_database()
|
|
|
|
def delete_series(self, series):
|
|
"""Drop a series on the InfluxDB server.
|
|
|
|
:param series: the name of the series to delete
|
|
:type series: string
|
|
:rtype: boolean
|
|
"""
|
|
url = "db/{0}/series/{1}".format(
|
|
self._database,
|
|
series
|
|
)
|
|
|
|
self.request(
|
|
url=url,
|
|
method='DELETE',
|
|
expected_response_code=204
|
|
)
|
|
|
|
return True
|
|
|
|
def get_list_series(self):
|
|
"""Get a list of all time series in a database."""
|
|
response = self._query('list series')
|
|
return [series[1] for series in response[0]['points']]
|
|
|
|
def get_list_continuous_queries(self):
|
|
"""Get a list of continuous queries."""
|
|
response = self._query('list continuous queries')
|
|
return [query[2] for query in response[0]['points']]
|
|
|
|
# Security
|
|
# get list of cluster admins
|
|
# curl http://localhost:8086/cluster_admins?u=root&p=root
|
|
|
|
# add cluster admin
|
|
# curl -X POST http://localhost:8086/cluster_admins?u=root&p=root \
|
|
# -d '{"name": "paul", "password": "i write teh docz"}'
|
|
|
|
# update cluster admin password
|
|
# curl -X POST http://localhost:8086/cluster_admins/paul?u=root&p=root \
|
|
# -d '{"password": "new pass"}'
|
|
|
|
# delete cluster admin
|
|
# curl -X DELETE http://localhost:8086/cluster_admins/paul?u=root&p=root
|
|
|
|
# Database admins, with a database name of site_dev
|
|
# get list of database admins
|
|
# curl http://localhost:8086/db/site_dev/admins?u=root&p=root
|
|
|
|
# add database admin
|
|
# curl -X POST http://localhost:8086/db/site_dev/admins?u=root&p=root \
|
|
# -d '{"name": "paul", "password": "i write teh docz"}'
|
|
|
|
# update database admin password
|
|
# curl -X POST http://localhost:8086/db/site_dev/admins/paul?u=root&p=root\
|
|
# -d '{"password": "new pass"}'
|
|
|
|
# delete database admin
|
|
# curl -X DELETE \
|
|
# http://localhost:8086/db/site_dev/admins/paul?u=root&p=root
|
|
|
|
def get_list_cluster_admins(self):
|
|
"""Get list of cluster admins."""
|
|
response = self.request(
|
|
url="cluster_admins",
|
|
method='GET',
|
|
expected_response_code=200
|
|
)
|
|
|
|
return response.json()
|
|
|
|
def add_cluster_admin(self, new_username, new_password):
|
|
"""Add cluster admin."""
|
|
data = {
|
|
'name': new_username,
|
|
'password': new_password
|
|
}
|
|
|
|
self.request(
|
|
url="cluster_admins",
|
|
method='POST',
|
|
data=data,
|
|
expected_response_code=200
|
|
)
|
|
|
|
return True
|
|
|
|
def update_cluster_admin_password(self, username, new_password):
|
|
"""Update cluster admin password."""
|
|
url = "cluster_admins/{0}".format(username)
|
|
|
|
data = {
|
|
'password': new_password
|
|
}
|
|
|
|
self.request(
|
|
url=url,
|
|
method='POST',
|
|
data=data,
|
|
expected_response_code=200
|
|
)
|
|
|
|
return True
|
|
|
|
def delete_cluster_admin(self, username):
|
|
"""Delete cluster admin."""
|
|
url = "cluster_admins/{0}".format(username)
|
|
|
|
self.request(
|
|
url=url,
|
|
method='DELETE',
|
|
expected_response_code=200
|
|
)
|
|
|
|
return True
|
|
|
|
def set_database_admin(self, username):
|
|
"""Set user as database admin."""
|
|
return self.alter_database_admin(username, True)
|
|
|
|
def unset_database_admin(self, username):
|
|
"""Unset user as database admin."""
|
|
return self.alter_database_admin(username, False)
|
|
|
|
def alter_database_admin(self, username, is_admin):
|
|
"""Alter the database admin."""
|
|
url = "db/{0}/users/{1}".format(self._database, username)
|
|
|
|
data = {'admin': is_admin}
|
|
|
|
self.request(
|
|
url=url,
|
|
method='POST',
|
|
data=data,
|
|
expected_response_code=200
|
|
)
|
|
|
|
return True
|
|
|
|
def get_list_database_admins(self):
|
|
"""Get list of database admins.
|
|
|
|
2013-11-08: This endpoint has not been implemented yet in ver0.0.8,
|
|
but it is documented in http://influxdb.org/docs/api/http.html.
|
|
See also: src/api/http/api.go:l57
|
|
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def add_database_admin(self, new_username, new_password):
|
|
"""Add cluster admin.
|
|
|
|
2013-11-08: This endpoint has not been implemented yet in ver0.0.8,
|
|
but it is documented in http://influxdb.org/docs/api/http.html.
|
|
See also: src/api/http/api.go:l57
|
|
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def update_database_admin_password(self, username, new_password):
|
|
"""Update database admin password.
|
|
|
|
2013-11-08: This endpoint has not been implemented yet in ver0.0.8,
|
|
but it is documented in http://influxdb.org/docs/api/http.html.
|
|
See also: src/api/http/api.go:l57
|
|
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def delete_database_admin(self, username):
|
|
"""Delete database admin.
|
|
|
|
2013-11-08: This endpoint has not been implemented yet in ver0.0.8,
|
|
but it is documented in http://influxdb.org/docs/api/http.html.
|
|
See also: src/api/http/api.go:l57
|
|
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
###
|
|
# Limiting User Access
|
|
|
|
# Database users
|
|
# get list of database users
|
|
# curl http://localhost:8086/db/site_dev/users?u=root&p=root
|
|
|
|
# add database user
|
|
# curl -X POST http://localhost:8086/db/site_dev/users?u=root&p=root \
|
|
# -d '{"name": "paul", "password": "i write teh docz"}'
|
|
|
|
# update database user password
|
|
# curl -X POST http://localhost:8086/db/site_dev/users/paul?u=root&p=root \
|
|
# -d '{"password": "new pass"}'
|
|
|
|
# delete database user
|
|
# curl -X DELETE http://localhost:8086/db/site_dev/users/paul?u=root&p=root
|
|
|
|
def get_database_users(self):
|
|
"""Get list of database users."""
|
|
url = "db/{0}/users".format(self._database)
|
|
|
|
response = self.request(
|
|
url=url,
|
|
method='GET',
|
|
expected_response_code=200
|
|
)
|
|
|
|
return response.json()
|
|
|
|
def add_database_user(self, new_username, new_password, permissions=None):
|
|
"""Add database user.
|
|
|
|
:param permissions: A ``(readFrom, writeTo)`` tuple
|
|
"""
|
|
url = "db/{0}/users".format(self._database)
|
|
|
|
data = {
|
|
'name': new_username,
|
|
'password': new_password
|
|
}
|
|
|
|
if permissions:
|
|
try:
|
|
data['readFrom'], data['writeTo'] = permissions
|
|
except (ValueError, TypeError):
|
|
raise TypeError(
|
|
"'permissions' must be (readFrom, writeTo) tuple"
|
|
)
|
|
|
|
self.request(
|
|
url=url,
|
|
method='POST',
|
|
data=data,
|
|
expected_response_code=200
|
|
)
|
|
|
|
return True
|
|
|
|
def update_database_user_password(self, username, new_password):
|
|
"""Update password."""
|
|
return self.alter_database_user(username, new_password)
|
|
|
|
def alter_database_user(self, username, password=None, permissions=None):
|
|
"""Alter a database user and/or their permissions.
|
|
|
|
:param permissions: A ``(readFrom, writeTo)`` tuple
|
|
:raise TypeError: if permissions cannot be read.
|
|
:raise ValueError: if neither password nor permissions provided.
|
|
"""
|
|
url = "db/{0}/users/{1}".format(self._database, username)
|
|
|
|
if not password and not permissions:
|
|
raise ValueError("Nothing to alter for user {0}.".format(username))
|
|
|
|
data = {}
|
|
|
|
if password:
|
|
data['password'] = password
|
|
|
|
if permissions:
|
|
try:
|
|
data['readFrom'], data['writeTo'] = permissions
|
|
except (ValueError, TypeError):
|
|
raise TypeError(
|
|
"'permissions' must be (readFrom, writeTo) tuple"
|
|
)
|
|
|
|
self.request(
|
|
url=url,
|
|
method='POST',
|
|
data=data,
|
|
expected_response_code=200
|
|
)
|
|
|
|
if username == self._username:
|
|
self._password = password
|
|
|
|
return True
|
|
|
|
def delete_database_user(self, username):
|
|
"""Delete database user."""
|
|
url = "db/{0}/users/{1}".format(self._database, username)
|
|
|
|
self.request(
|
|
url=url,
|
|
method='DELETE',
|
|
expected_response_code=200
|
|
)
|
|
|
|
return True
|
|
|
|
# update the user by POSTing to db/site_dev/users/paul
|
|
|
|
def update_permission(self, username, json_body):
|
|
"""Update read/write permission.
|
|
|
|
2013-11-08: This endpoint has not been implemented yet in ver0.0.8,
|
|
but it is documented in http://influxdb.org/docs/api/http.html.
|
|
See also: src/api/http/api.go:l57
|
|
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def send_packet(self, packet):
|
|
"""Send a UDP packet along the wire."""
|
|
data = json.dumps(packet)
|
|
byte = data.encode('utf-8')
|
|
self.udp_socket.sendto(byte, (self._host, self._udp_port))
|