Skip to content

Commit 411323b

Browse files
committed
Merge pull request influxdata#179 from georgijd/f-more-operations
Add more operations to the client (Thanks @georgijd !)
2 parents ab2e66c + 731c4e0 commit 411323b

File tree

3 files changed

+335
-0
lines changed

3 files changed

+335
-0
lines changed

influxdb/client.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,43 @@ def create_retention_policy(self, name, duration, replication,
470470

471471
self.query(query_string)
472472

473+
def alter_retention_policy(self, name, database=None,
474+
duration=None, replication=None, default=None):
475+
"""Mofidy an existing retention policy for a database.
476+
477+
:param name: the name of the retention policy to modify
478+
:type name: str
479+
:param database: the database for which the retention policy is
480+
modified. Defaults to current client's database
481+
:type database: str
482+
:param duration: the new duration of the existing retention policy.
483+
Durations such as 1h, 90m, 12h, 7d, and 4w, are all supported
484+
and mean 1 hour, 90 minutes, 12 hours, 7 day, and 4 weeks,
485+
respectively. For infinite retention – meaning the data will
486+
never be deleted – use 'INF' for duration.
487+
The minimum retention period is 1 hour.
488+
:type duration: str
489+
:param replication: the new replication of the existing
490+
retention policy
491+
:type replication: str
492+
:param default: whether or not to set the modified policy as default
493+
:type default: bool
494+
495+
.. note:: at least one of duration, replication, or default flag
496+
should be set. Otherwise the operation will fail.
497+
"""
498+
query_string = (
499+
"ALTER RETENTION POLICY {} ON {}"
500+
).format(name, database or self._database)
501+
if duration:
502+
query_string += " DURATION {}".format(duration)
503+
if replication:
504+
query_string += " REPLICATION {}".format(replication)
505+
if default is True:
506+
query_string += " DEFAULT"
507+
508+
self.query(query_string)
509+
473510
def get_list_retention_policies(self, database=None):
474511
"""Get the list of retention policies for a database.
475512
@@ -581,6 +618,62 @@ def delete_series(self, name, database=None):
581618
database = database or self._database
582619
self.query('DROP SERIES \"%s\"' % name, database=database)
583620

621+
def grant_admin_privileges(self, username):
622+
"""Grant cluster administration privileges to an user.
623+
624+
:param username: the username to grant privileges to
625+
:type username: str
626+
627+
.. note:: Only a cluster administrator can create/ drop databases
628+
and manage users.
629+
"""
630+
text = "GRANT ALL PRIVILEGES TO {}".format(username)
631+
self.query(text)
632+
633+
def revoke_admin_privileges(self, username):
634+
"""Revoke cluster administration privileges from an user.
635+
636+
:param username: the username to revoke privileges from
637+
:type username: str
638+
639+
.. note:: Only a cluster administrator can create/ drop databases
640+
and manage users.
641+
"""
642+
text = "REVOKE ALL PRIVILEGES FROM {}".format(username)
643+
self.query(text)
644+
645+
def grant_privilege(self, privilege, database, username):
646+
"""Grant a privilege on a database to an user.
647+
648+
:param privilege: the privilege to grant, one of 'read', 'write'
649+
or 'all'. The string is case-insensitive
650+
:type privilege: str
651+
:param database: the database to grant the privilege on
652+
:type database: str
653+
:param username: the username to grant the privilege to
654+
:type username: str
655+
"""
656+
text = "GRANT {} ON {} TO {}".format(privilege,
657+
database,
658+
username)
659+
self.query(text)
660+
661+
def revoke_privilege(self, privilege, database, username):
662+
"""Revoke a privilege on a database from an user.
663+
664+
:param privilege: the privilege to revoke, one of 'read', 'write'
665+
or 'all'. The string is case-insensitive
666+
:type privilege: str
667+
:param database: the database to revoke the privilege on
668+
:type database: str
669+
:param username: the username to revoke the privilege from
670+
:type username: str
671+
"""
672+
text = "REVOKE {} ON {} FROM {}".format(privilege,
673+
database,
674+
username)
675+
self.query(text)
676+
584677
def send_packet(self, packet):
585678
"""Send an UDP packet.
586679

tests/influxdb/client_test.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,44 @@ def test_create_retention_policy(self):
490490
'db duration 1d replication 4'
491491
)
492492

493+
def test_alter_retention_policy(self):
494+
example_response = '{"results":[{}]}'
495+
496+
with requests_mock.Mocker() as m:
497+
m.register_uri(
498+
requests_mock.GET,
499+
"http://localhost:8086/query",
500+
text=example_response
501+
)
502+
# Test alter duration
503+
self.cli.alter_retention_policy('somename', 'db',
504+
duration='4d')
505+
self.assertEqual(
506+
m.last_request.qs['q'][0],
507+
'alter retention policy somename on db duration 4d'
508+
)
509+
# Test alter replication
510+
self.cli.alter_retention_policy('somename', 'db',
511+
replication=4)
512+
self.assertEqual(
513+
m.last_request.qs['q'][0],
514+
'alter retention policy somename on db replication 4'
515+
)
516+
517+
# Test alter default
518+
self.cli.alter_retention_policy('somename', 'db',
519+
default=True)
520+
self.assertEqual(
521+
m.last_request.qs['q'][0],
522+
'alter retention policy somename on db default'
523+
)
524+
525+
@raises(Exception)
526+
def test_alter_retention_policy_invalid(self):
527+
cli = InfluxDBClient('host', 8086, 'username', 'password')
528+
with _mocked_session(cli, 'get', 400):
529+
self.cli.alter_retention_policy('somename', 'db')
530+
493531
def test_get_list_retention_policies(self):
494532
example_response = \
495533
'{"results": [{"series": [{"values": [["fsfdsdf", "24h0m0s", 2]],'\
@@ -586,6 +624,94 @@ def test_get_list_users_empty(self):
586624

587625
self.assertListEqual(self.cli.get_list_users(), [])
588626

627+
def test_grant_admin_privileges(self):
628+
example_response = '{"results":[{}]}'
629+
630+
with requests_mock.Mocker() as m:
631+
m.register_uri(
632+
requests_mock.GET,
633+
"http://localhost:8086/query",
634+
text=example_response
635+
)
636+
self.cli.grant_admin_privileges('test')
637+
638+
self.assertEqual(
639+
m.last_request.qs['q'][0],
640+
'grant all privileges to test'
641+
)
642+
643+
@raises(Exception)
644+
def test_grant_admin_privileges_invalid(self):
645+
cli = InfluxDBClient('host', 8086, 'username', 'password')
646+
with _mocked_session(cli, 'get', 400):
647+
self.cli.grant_admin_privileges('')
648+
649+
def test_revoke_admin_privileges(self):
650+
example_response = '{"results":[{}]}'
651+
652+
with requests_mock.Mocker() as m:
653+
m.register_uri(
654+
requests_mock.GET,
655+
"http://localhost:8086/query",
656+
text=example_response
657+
)
658+
self.cli.revoke_admin_privileges('test')
659+
660+
self.assertEqual(
661+
m.last_request.qs['q'][0],
662+
'revoke all privileges from test'
663+
)
664+
665+
@raises(Exception)
666+
def test_revoke_admin_privileges_invalid(self):
667+
cli = InfluxDBClient('host', 8086, 'username', 'password')
668+
with _mocked_session(cli, 'get', 400):
669+
self.cli.revoke_admin_privileges('')
670+
671+
def test_grant_privilege(self):
672+
example_response = '{"results":[{}]}'
673+
674+
with requests_mock.Mocker() as m:
675+
m.register_uri(
676+
requests_mock.GET,
677+
"http://localhost:8086/query",
678+
text=example_response
679+
)
680+
self.cli.grant_privilege('read', 'testdb', 'test')
681+
682+
self.assertEqual(
683+
m.last_request.qs['q'][0],
684+
'grant read on testdb to test'
685+
)
686+
687+
@raises(Exception)
688+
def test_grant_privilege_invalid(self):
689+
cli = InfluxDBClient('host', 8086, 'username', 'password')
690+
with _mocked_session(cli, 'get', 400):
691+
self.cli.grant_privilege('', 'testdb', 'test')
692+
693+
def test_revoke_privilege(self):
694+
example_response = '{"results":[{}]}'
695+
696+
with requests_mock.Mocker() as m:
697+
m.register_uri(
698+
requests_mock.GET,
699+
"http://localhost:8086/query",
700+
text=example_response
701+
)
702+
self.cli.revoke_privilege('read', 'testdb', 'test')
703+
704+
self.assertEqual(
705+
m.last_request.qs['q'][0],
706+
'revoke read on testdb from test'
707+
)
708+
709+
@raises(Exception)
710+
def test_revoke_privilege_invalid(self):
711+
cli = InfluxDBClient('host', 8086, 'username', 'password')
712+
with _mocked_session(cli, 'get', 400):
713+
self.cli.revoke_privilege('', 'testdb', 'test')
714+
589715

590716
class FakeClient(InfluxDBClient):
591717
fail = False

tests/influxdb/client_test_with_server.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,67 @@ def test_drop_user_invalid(self):
376376
'found invalid, expected',
377377
ctx.exception.content)
378378

379+
def test_grant_admin_privileges(self):
380+
self.cli.create_user('test', 'test')
381+
self.assertEqual([{'user': 'test', 'admin': False}],
382+
self.cli.get_list_users())
383+
self.cli.grant_admin_privileges('test')
384+
self.assertEqual([{'user': 'test', 'admin': True}],
385+
self.cli.get_list_users())
386+
387+
def test_grant_admin_privileges_invalid(self):
388+
with self.assertRaises(InfluxDBClientError) as ctx:
389+
self.cli.grant_admin_privileges('')
390+
self.assertEqual(400, ctx.exception.code)
391+
self.assertIn('{"error":"error parsing query: ',
392+
ctx.exception.content)
393+
394+
def test_revoke_admin_privileges(self):
395+
self.cli.create_user('test', 'test')
396+
self.cli.grant_admin_privileges('test')
397+
self.assertEqual([{'user': 'test', 'admin': True}],
398+
self.cli.get_list_users())
399+
self.cli.revoke_admin_privileges('test')
400+
self.assertEqual([{'user': 'test', 'admin': False}],
401+
self.cli.get_list_users())
402+
403+
def test_revoke_admin_privileges_invalid(self):
404+
with self.assertRaises(InfluxDBClientError) as ctx:
405+
self.cli.revoke_admin_privileges('')
406+
self.assertEqual(400, ctx.exception.code)
407+
self.assertIn('{"error":"error parsing query: ',
408+
ctx.exception.content)
409+
410+
def test_grant_privilege(self):
411+
self.cli.create_user('test', 'test')
412+
self.cli.create_database('testdb')
413+
self.cli.grant_privilege('all', 'testdb', 'test')
414+
# TODO: when supported by InfluxDB, check if privileges are granted
415+
416+
def test_grant_privilege_invalid(self):
417+
self.cli.create_user('test', 'test')
418+
self.cli.create_database('testdb')
419+
with self.assertRaises(InfluxDBClientError) as ctx:
420+
self.cli.grant_privilege('', 'testdb', 'test')
421+
self.assertEqual(400, ctx.exception.code)
422+
self.assertIn('{"error":"error parsing query: ',
423+
ctx.exception.content)
424+
425+
def test_revoke_privilege(self):
426+
self.cli.create_user('test', 'test')
427+
self.cli.create_database('testdb')
428+
self.cli.revoke_privilege('all', 'testdb', 'test')
429+
# TODO: when supported by InfluxDB, check if privileges are revoked
430+
431+
def test_revoke_privilege_invalid(self):
432+
self.cli.create_user('test', 'test')
433+
self.cli.create_database('testdb')
434+
with self.assertRaises(InfluxDBClientError) as ctx:
435+
self.cli.revoke_privilege('', 'testdb', 'test')
436+
self.assertEqual(400, ctx.exception.code)
437+
self.assertIn('{"error":"error parsing query: ',
438+
ctx.exception.content)
439+
379440

380441
############################################################################
381442

@@ -657,6 +718,61 @@ def test_create_retention_policy(self):
657718
rsp
658719
)
659720

721+
def test_alter_retention_policy(self):
722+
self.cli.create_retention_policy('somename', '1d', 1)
723+
724+
# Test alter duration
725+
self.cli.alter_retention_policy('somename', 'db',
726+
duration='4d')
727+
rsp = self.cli.get_list_retention_policies()
728+
self.assertEqual(
729+
[{'duration': '0', 'default': True,
730+
'replicaN': 1, 'name': 'default'},
731+
{'duration': '96h0m0s', 'default': False,
732+
'replicaN': 1, 'name': 'somename'}],
733+
rsp
734+
)
735+
736+
# Test alter replication
737+
self.cli.alter_retention_policy('somename', 'db',
738+
replication=4)
739+
rsp = self.cli.get_list_retention_policies()
740+
self.assertEqual(
741+
[{'duration': '0', 'default': True,
742+
'replicaN': 1, 'name': 'default'},
743+
{'duration': '96h0m0s', 'default': False,
744+
'replicaN': 4, 'name': 'somename'}],
745+
rsp
746+
)
747+
748+
# Test alter default
749+
self.cli.alter_retention_policy('somename', 'db',
750+
default=True)
751+
rsp = self.cli.get_list_retention_policies()
752+
self.assertEqual(
753+
[{'duration': '0', 'default': False,
754+
'replicaN': 1, 'name': 'default'},
755+
{'duration': '96h0m0s', 'default': True,
756+
'replicaN': 4, 'name': 'somename'}],
757+
rsp
758+
)
759+
760+
def test_alter_retention_policy_invalid(self):
761+
self.cli.create_retention_policy('somename', '1d', 1)
762+
with self.assertRaises(InfluxDBClientError) as ctx:
763+
self.cli.alter_retention_policy('somename', 'db')
764+
self.assertEqual(400, ctx.exception.code)
765+
self.assertIn('{"error":"error parsing query: ',
766+
ctx.exception.content)
767+
rsp = self.cli.get_list_retention_policies()
768+
self.assertEqual(
769+
[{'duration': '0', 'default': True,
770+
'replicaN': 1, 'name': 'default'},
771+
{'duration': '24h0m0s', 'default': False,
772+
'replicaN': 1, 'name': 'somename'}],
773+
rsp
774+
)
775+
660776
def test_issue_143(self):
661777
pt = partial(point, 'a_serie_name', timestamp='2015-03-30T16:16:37Z')
662778
pts = [

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy