Skip to content

Commit b73cc91

Browse files
authored
Merge pull request #2125 from DanCech/multiTag
Multi series tag/delete support
2 parents 5921fe0 + aa2e518 commit b73cc91

File tree

7 files changed

+279
-127
lines changed

7 files changed

+279
-127
lines changed

docs/tags.rst

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,19 @@ The default settings (above) will connect to a local Redis server on the default
113113
HTTP(S) TagDB
114114
^^^^^^^^^^^^^
115115

116-
The HTTP(S) TagDB is used to delegate all tag operations to an external server that implements the Graphite tagging HTTP API. It can be used in clustered graphite scenarios, or with custom data stores. It is selected by setting ``TAGDB='graphite.tags.http.HttpTagDB'`` in `local_settings.py`. There are 3 additional config settings for the HTTP(S) TagDB::
116+
The HTTP(S) TagDB is used to delegate all tag operations to an external server that implements the Graphite tagging HTTP API. It can be used in clustered graphite scenarios, or with custom data stores. It is selected by setting ``TAGDB='graphite.tags.http.HttpTagDB'`` in `local_settings.py`. There are 4 additional config settings for the HTTP(S) TagDB::
117117

118118
TAGDB_HTTP_URL = 'https://another.server'
119119
TAGDB_HTTP_USER = ''
120120
TAGDB_HTTP_PASSWORD = ''
121+
TAGDB_HTTP_AUTOCOMPLETE = False
121122

122123
The ``TAGDB_HTTP_URL`` is required. ``TAGDB_HTTP_USER`` and ``TAGDB_HTTP_PASSWORD`` are optional and if specified will be used to send a Basic Authorization header in all requests.
123124

125+
``TAGDB_HTTP_AUTOCOMPLETE`` is also optional, if set to ``True`` auto-complete requests will be forwarded to the remote TagDB, otherwise calls to `/tags/findSeries`, `/tags` & `/tags/<tag>` will be used to provide auto-complete functionality.
126+
127+
If ``REMOTE_STORE_FORWARD_HEADERS`` is defined, those headers will also be forwarded to the remote TagDB.
128+
124129
Adding Series to the TagDB
125130
--------------------------
126131
Normally `carbon` will take care of this, it submits all new series to the TagDB, and periodically re-submits all series to ensure that the TagDB is kept up to date. There are 2 `carbon` configuration settings related to tagging; the `GRAPHITE_URL` setting specifies the url of your graphite-web installation (default `http://127.0.0.1:8000`), and the `TAG_UPDATE_INTERVAL` setting specifies how often each series should be re-submitted to the TagDB (default is every 100th update).
@@ -136,6 +141,22 @@ Series can be submitted via HTTP POST using command-line tools such as ``curl``
136141
137142
This endpoint returns the canonicalized version of the path, with the tags sorted in alphabetical order.
138143

144+
To add multiple series with a single HTTP request, use the ``/tags/tagMultiSeries`` endpoint, which support multiple ``path`` parameters:
145+
146+
.. code-block:: none
147+
148+
$ curl -X POST "http://graphite/tags/tagMultiSeries" \
149+
--data-urlencode 'path=disk.used;rack=a1;datacenter=dc1;server=web01' \
150+
--data-urlencode 'path=disk.used;rack=a1;datacenter=dc1;server=web02' \
151+
--data-urlencode 'pretty=1'
152+
153+
[
154+
"disk.used;datacenter=dc1;rack=a1;server=web01",
155+
"disk.used;datacenter=dc1;rack=a1;server=web02"
156+
]
157+
158+
This endpoint returns a list of the canonicalized paths, in the same order they are specified.
159+
139160
Exploring Tags
140161
--------------
141162
You can use the HTTP api to get lists of defined tags, values for each tag, and to find series using the same logic as the `seriesByTag <functions.html#graphite.render.functions.seriesByTag>`_ function.
@@ -309,3 +330,13 @@ Series can be deleted via HTTP POST to the `/tags/delSeries` endpoint:
309330
--data-urlencode 'path=disk.used;datacenter=dc1;rack=a1;server=web01'
310331
311332
true
333+
334+
To delete multiple series at once pass multiple ``path`` parameters:
335+
336+
.. code-block:: none
337+
338+
$ curl -X POST "http://graphite/tags/delSeries" \
339+
--data-urlencode 'path=disk.used;datacenter=dc1;rack=a1;server=web01' \
340+
--data-urlencode 'path=disk.used;datacenter=dc1;rack=a1;server=web02'
341+
342+
true

webapp/graphite/storage.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def __init__(self, finders=None, tagdb=None):
5454

5555
if tagdb is None:
5656
tagdb = settings.TAGDB
57-
self.tagdb = get_tagdb(tagdb) if tagdb else None
57+
self.tagdb = get_tagdb(tagdb)
5858

5959
def get_finders(self, local=False):
6060
for finder in self.finders:

webapp/graphite/tags/base.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,26 @@ def tag_series(self, series, requestContext=None):
147147
Enter series into database. Accepts a series string, upserts into the TagDB and returns the canonicalized series name.
148148
"""
149149

150+
def tag_multi_series(self, seriesList, requestContext=None):
151+
"""
152+
Enter series into database. Accepts a list of series strings, upserts into the TagDB and returns a list of canonicalized series names.
153+
"""
154+
return [self.tag_series(series, requestContext) for series in seriesList]
155+
150156
@abc.abstractmethod
151157
def del_series(self, series, requestContext=None):
152158
"""
153159
Remove series from database. Accepts a series string and returns True
154160
"""
155161

162+
def del_multi_series(self, seriesList, requestContext=None):
163+
"""
164+
Remove series from database. Accepts a list of series strings, removes them from the TagDB and returns True
165+
"""
166+
for series in seriesList:
167+
self.del_series(series, requestContext)
168+
return True
169+
156170
def auto_complete_tags(self, exprs, tagPrefix=None, limit=None, requestContext=None):
157171
"""
158172
Return auto-complete suggestions for tags based on the matches for the specified expressions, optionally filtered by tag prefix

webapp/graphite/tags/http.py

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from __future__ import absolute_import
22

3-
from urllib import quote
43
import json
54

65
from graphite.http_pool import http
@@ -19,10 +18,7 @@ def __init__(self, settings, *args, **kwargs):
1918
self.username = settings.TAGDB_HTTP_USER
2019
self.password = settings.TAGDB_HTTP_PASSWORD
2120

22-
def request(self, method, url, fields=None, requestContext=None):
23-
if not fields:
24-
fields = {}
25-
21+
def request(self, method, url, fields, requestContext=None):
2622
headers = requestContext.get('forwardHeaders') if requestContext else {}
2723
if 'Authorization' not in headers and self.username and self.password:
2824
headers['Authorization'] = 'Basic ' + ('%s:%s' % (self.username, self.password)).encode('base64')
@@ -35,8 +31,11 @@ def request(self, method, url, fields=None, requestContext=None):
3531
timeout=self.settings.REMOTE_FIND_TIMEOUT,
3632
)
3733

34+
if result.status == 400:
35+
raise ValueError(json.loads(result.data.decode('utf-8')).get('error'))
36+
3837
if result.status != 200:
39-
raise Exception('HTTP Error from remote tagdb: %s' % result.status)
38+
raise Exception('HTTP Error from remote tagdb: %s %s' % (result.status, result.data))
4039

4140
return json.loads(result.data.decode('utf-8'))
4241

@@ -51,8 +50,9 @@ def find_series_cachekey(self, tags, requestContext=None):
5150

5251
def _find_series(self, tags, requestContext=None):
5352
return self.request(
54-
'GET',
55-
'/tags/findSeries?' + '&'.join([('expr=%s' % quote(tag)) for tag in tags]),
53+
'POST',
54+
'/tags/findSeries',
55+
{'expr': tags},
5656
requestContext=requestContext,
5757
)
5858

@@ -83,9 +83,15 @@ def list_values(self, tag, valueFilter=None, limit=None, requestContext=None):
8383
def tag_series(self, series, requestContext=None):
8484
return self.request('POST', '/tags/tagSeries', {'path': series}, requestContext)
8585

86+
def tag_multi_series(self, seriesList, requestContext=None):
87+
return self.request('POST', '/tags/tagMultiSeries', {'path': seriesList}, requestContext)
88+
8689
def del_series(self, series, requestContext=None):
8790
return self.request('POST', '/tags/delSeries', {'path': series}, requestContext)
8891

92+
def del_multi_series(self, seriesList, requestContext=None):
93+
return self.request('POST', '/tags/delSeries', {'path': seriesList}, requestContext)
94+
8995
def auto_complete_tags(self, exprs, tagPrefix=None, limit=None, requestContext=None):
9096
"""
9197
Return auto-complete suggestions for tags based on the matches for the specified expressions, optionally filtered by tag prefix
@@ -97,10 +103,13 @@ def auto_complete_tags(self, exprs, tagPrefix=None, limit=None, requestContext=N
97103
if limit is None:
98104
limit = self.settings.TAGDB_AUTOCOMPLETE_LIMIT
99105

100-
url = '/tags/autoComplete/tags?tagPrefix=' + quote(tagPrefix or '') + '&limit=' + quote(str(limit)) + \
101-
'&' + '&'.join([('expr=%s' % quote(expr or '')) for expr in exprs])
106+
fields = {
107+
'tagPrefix': tagPrefix or '',
108+
'limit': str(limit),
109+
'expr': exprs,
110+
}
102111

103-
return self.request('GET', url)
112+
return self.request('POST', '/tags/autoComplete/tags', fields, requestContext)
104113

105114
def auto_complete_values(self, exprs, tag, valuePrefix=None, limit=None, requestContext=None):
106115
"""
@@ -113,7 +122,11 @@ def auto_complete_values(self, exprs, tag, valuePrefix=None, limit=None, request
113122
if limit is None:
114123
limit = self.settings.TAGDB_AUTOCOMPLETE_LIMIT
115124

116-
url = '/tags/autoComplete/values?tag=' + quote(tag or '') + '&valuePrefix=' + quote(valuePrefix or '') + \
117-
'&limit=' + quote(str(limit)) + '&' + '&'.join([('expr=%s' % quote(expr or '')) for expr in exprs])
125+
fields = {
126+
'tag': tag or '',
127+
'valuePrefix': valuePrefix or '',
128+
'limit': str(limit),
129+
'expr': exprs,
130+
}
118131

119-
return self.request('GET', url)
132+
return self.request('POST', '/tags/autoComplete/values', fields, requestContext)

webapp/graphite/tags/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
urlpatterns = [
1919
url('tagSeries', views.tagSeries, name='tagSeries'),
20+
url('tagMultiSeries', views.tagMultiSeries, name='tagMultiSeries'),
2021
url('delSeries', views.delSeries, name='delSeries'),
2122
url('findSeries', views.findSeries, name='findSeries'),
2223
url('autoComplete/tags', views.autoCompleteTags, name='tagAutoCompleteTags'),

0 commit comments

Comments
 (0)