Skip to content

Release/2.1.0 #595

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 35 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
ca3d9eb
Add exception logging in Script
kkedziak-splunk Feb 22, 2024
d9492f5
Merge branch 'master' into feature/improve_logging
kkedziak-splunk Apr 5, 2024
2869237
Revert CHANGELOG.md
kkedziak-splunk Apr 5, 2024
e5f7af0
Revert README.md
kkedziak-splunk Apr 5, 2024
3dab0b5
Update __init__.py
kkedziak-splunk Apr 5, 2024
be7dfe0
Refactor comment
kkedziak-splunk May 8, 2024
40e7b2a
DVPL-0: don't use latest splunk version because of HEC token issue
maszyk99 May 16, 2024
75806e6
Merge pull request #576 from splunk/DVPL-0/change-splunk-versions
maszyk99 May 17, 2024
e4a07be
Merge branch 'refs/heads/develop' into feature/improve_logging
kkedziak-splunk May 20, 2024
3ef0519
Change
kkedziak-splunk May 21, 2024
8d74bd5
Merge pull request #559 from kkedziak-splunk/feature/improve_logging
maszyk99 May 23, 2024
1a2d17f
Add python2/3 utils file back
maszyk99 Jun 25, 2024
ba04e1b
Merge pull request #578 from splunk/DVPL-0/add-six-py-back
maszyk99 Jun 27, 2024
a02b2ce
update pipelines so they would be testing python3.13
szymonjas Oct 15, 2024
a4085e0
DVPL-0: enable manual workflow trigger
maszyk99 Oct 16, 2024
6b55844
DVPL-0: fix test pipeline
maszyk99 Oct 20, 2024
eeb1ecf
Merge pull request #587 from splunk/DVPL-0/enable-manual-workflow-tri…
maszyk99 Oct 20, 2024
93f51c4
Add Service.macros
Synse Feb 13, 2023
6de12b1
Add tests for macros
Synse Feb 13, 2023
e7ba1c1
Merge pull request #589 from splunk/test-macros-pr
maszyk99 Oct 21, 2024
6ec2559
remove commands used to run test directly from setup.py
szymonjas Oct 24, 2024
1b44135
modify tox ini not to use deprecated libraries for unittests and add …
szymonjas Oct 24, 2024
97e1b60
Merge remote-tracking branch 'origin/develop' into DVPL-11087-Python3.13
szymonjas Oct 24, 2024
2f2e174
modify testing traceback so it would filter out additions to tracback…
szymonjas Oct 24, 2024
e3f3968
Revert "DVPL-0: don't use latest splunk version because of HEC token …
maszyk99 Oct 25, 2024
0c9469d
Merge pull request #593 from splunk/DVPL-0/update-splunk-image
szymonjas Oct 28, 2024
a8948f8
resolve conflicts
szymonjas Oct 29, 2024
41e8948
Merge remote-tracking branch 'origin/develop' into VULN-17383
szymonjas Oct 29, 2024
2a8931e
Merge pull request #590 from splunk/VULN-17383
szymonjas Oct 29, 2024
2b90307
Merge pull request #592 from splunk/DVPL-11087-Python3.13
szymonjas Oct 29, 2024
daf1bcd
Merge remote-tracking branch 'origin/master' into develop
szymonjas Oct 29, 2024
f637ade
use SSL DEFAULT CONTEX, use tls version min 1.2, allow for usage of S…
szymonjas Sep 25, 2024
180cdd2
Add changelog
szymonjas Oct 29, 2024
25c44e9
update changelog
szymonjas Oct 29, 2024
d36db5e
update ReadMe
szymonjas Oct 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 95 additions & 1 deletion splunklib/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
PATH_JOBS = "search/jobs/"
PATH_JOBS_V2 = "search/v2/jobs/"
PATH_LOGGER = "/services/server/logger/"
PATH_MACROS = "configs/conf-macros/"
PATH_MESSAGES = "messages/"
PATH_MODULAR_INPUTS = "data/modular-inputs"
PATH_ROLES = "authorization/roles/"
Expand Down Expand Up @@ -667,6 +668,15 @@ def saved_searches(self):
"""
return SavedSearches(self)

@property
def macros(self):
"""Returns the collection of macros.

:return: A :class:`Macros` collection of :class:`Macro`
entities.
"""
return Macros(self)

@property
def settings(self):
"""Returns the configuration settings for this instance of Splunk.
Expand Down Expand Up @@ -3440,6 +3450,90 @@ def create(self, name, search, **kwargs):
return Collection.create(self, name, search=search, **kwargs)


class Macro(Entity):
"""This class represents a search macro."""
def __init__(self, service, path, **kwargs):
Entity.__init__(self, service, path, **kwargs)

@property
def args(self):
"""Returns the macro arguments.
:return: The macro arguments.
:rtype: ``string``
"""
return self._state.content.get('args', '')

@property
def definition(self):
"""Returns the macro definition.
:return: The macro definition.
:rtype: ``string``
"""
return self._state.content.get('definition', '')

@property
def errormsg(self):
"""Returns the validation error message for the macro.
:return: The validation error message for the macro.
:rtype: ``string``
"""
return self._state.content.get('errormsg', '')

@property
def iseval(self):
"""Returns the eval-based definition status of the macro.
:return: The iseval value for the macro.
:rtype: ``string``
"""
return self._state.content.get('iseval', '0')

def update(self, definition=None, **kwargs):
"""Updates the server with any changes you've made to the current macro
along with any additional arguments you specify.
:param `definition`: The macro definition (optional).
:type definition: ``string``
:param `kwargs`: Additional arguments (optional). Available parameters are:
'disabled', 'iseval', 'validation', and 'errormsg'.
:type kwargs: ``dict``
:return: The :class:`Macro`.
"""
# Updates to a macro *require* that the definition be
# passed, so we pass the current definition if a value wasn't
# provided by the caller.
if definition is None: definition = self.content.definition
Entity.update(self, definition=definition, **kwargs)
return self

@property
def validation(self):
"""Returns the validation expression for the macro.
:return: The validation expression for the macro.
:rtype: ``string``
"""
return self._state.content.get('validation', '')


class Macros(Collection):
"""This class represents a collection of macros. Retrieve this
collection using :meth:`Service.macros`."""
def __init__(self, service):
Collection.__init__(
self, service, PATH_MACROS, item=Macro)

def create(self, name, definition, **kwargs):
""" Creates a macro.
:param name: The name for the macro.
:type name: ``string``
:param definition: The macro definition.
:type definition: ``string``
:param kwargs: Additional arguments (optional). Available parameters are:
'disabled', 'iseval', 'validation', and 'errormsg'.
:type kwargs: ``dict``
:return: The :class:`Macros` collection.
"""
return Collection.create(self, name, definition=definition, **kwargs)


class Settings(Entity):
"""This class represents configuration settings for a Splunk service.
Retrieve this collection using :meth:`Service.settings`."""
Expand Down Expand Up @@ -3905,4 +3999,4 @@ def batch_save(self, *documents):
data = json.dumps(documents)

return json.loads(
self._post('batch_save', headers=KVStoreCollectionData.JSON_HEADER, body=data).body.read().decode('utf-8'))
self._post('batch_save', headers=KVStoreCollectionData.JSON_HEADER, body=data).body.read().decode('utf-8'))
164 changes: 164 additions & 0 deletions tests/test_macro.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
#!/usr/bin/env python
#
# Copyright 2011-2015 Splunk, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"): you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from __future__ import absolute_import
from tests import testlib
import logging

import splunklib.client as client

import pytest

@pytest.mark.smoke
class TestMacro(testlib.SDKTestCase):
def setUp(self):
super(TestMacro, self).setUp()
macros = self.service.macros
logging.debug("Macros namespace: %s", macros.service.namespace)
self.macro_name = testlib.tmpname()
definition = '| eval test="123"'
self.macro = macros.create(self.macro_name, definition)

def tearDown(self):
super(TestMacro, self).setUp()
for macro in self.service.macros:
if macro.name.startswith('delete-me'):
self.service.macros.delete(macro.name)

def check_macro(self, macro):
self.check_entity(macro)
expected_fields = ['definition',
'iseval',
'args',
'validation',
'errormsg']
for f in expected_fields:
macro[f]
is_eval = macro.iseval
self.assertTrue(is_eval == '1' or is_eval == '0')

def test_create(self):
self.assertTrue(self.macro_name in self.service.macros)
self.check_macro(self.macro)

def test_create_with_args(self):
macro_name = testlib.tmpname() + '(1)'
definition = '| eval value="$value$"'
kwargs = {
'args': 'value',
'validation': '$value$ > 10',
'errormsg': 'value must be greater than 10'
}
macro = self.service.macros.create(macro_name, definition=definition, **kwargs)
self.assertTrue(macro_name in self.service.macros)
self.check_macro(macro)
self.assertEqual(macro.iseval, '0')
self.assertEqual(macro.args, kwargs.get('args'))
self.assertEqual(macro.validation, kwargs.get('validation'))
self.assertEqual(macro.errormsg, kwargs.get('errormsg'))
self.service.macros.delete(macro_name)

def test_delete(self):
self.assertTrue(self.macro_name in self.service.macros)
self.service.macros.delete(self.macro_name)
self.assertFalse(self.macro_name in self.service.macros)
self.assertRaises(client.HTTPError,
self.macro.refresh)

def test_update(self):
new_definition = '| eval updated="true"'
self.macro.update(definition=new_definition)
self.macro.refresh()
self.assertEqual(self.macro['definition'], new_definition)

is_eval = testlib.to_bool(self.macro['iseval'])
self.macro.update(iseval=not is_eval)
self.macro.refresh()
self.assertEqual(testlib.to_bool(self.macro['iseval']), not is_eval)

def test_cannot_update_name(self):
new_name = self.macro_name + '-alteration'
self.assertRaises(client.IllegalOperationException,
self.macro.update, name=new_name)

def test_name_collision(self):
opts = self.opts.kwargs.copy()
opts['owner'] = '-'
opts['app'] = '-'
opts['sharing'] = 'user'
service = client.connect(**opts)
logging.debug("Namespace for collision testing: %s", service.namespace)
macros = service.macros
name = testlib.tmpname()

dispatch1 = '| eval macro_one="1"'
dispatch2 = '| eval macro_two="2"'
namespace1 = client.namespace(app='search', sharing='app')
namespace2 = client.namespace(owner='admin', app='search', sharing='user')
new_macro2 = macros.create(
name, dispatch2,
namespace=namespace1)
new_macro1 = macros.create(
name, dispatch1,
namespace=namespace2)

self.assertRaises(client.AmbiguousReferenceException,
macros.__getitem__, name)
macro1 = macros[name, namespace1]
self.check_macro(macro1)
macro1.update(**{'definition': '| eval number=1'})
macro1.refresh()
self.assertEqual(macro1['definition'], '| eval number=1')
macro2 = macros[name, namespace2]
macro2.update(**{'definition': '| eval number=2'})
macro2.refresh()
self.assertEqual(macro2['definition'], '| eval number=2')
self.check_macro(macro2)

def test_no_equality(self):
self.assertRaises(client.IncomparableException,
self.macro.__eq__, self.macro)

def test_acl(self):
self.assertEqual(self.macro.access["perms"], None)
self.macro.acl_update(sharing="app", owner="admin", **{"perms.read": "admin, nobody"})
self.assertEqual(self.macro.access["owner"], "admin")
self.assertEqual(self.macro.access["sharing"], "app")
self.assertEqual(self.macro.access["perms"]["read"], ['admin', 'nobody'])

def test_acl_fails_without_sharing(self):
self.assertRaisesRegex(
ValueError,
"Required argument 'sharing' is missing.",
self.macro.acl_update,
owner="admin", app="search", **{"perms.read": "admin, nobody"}
)

def test_acl_fails_without_owner(self):
self.assertRaisesRegex(
ValueError,
"Required argument 'owner' is missing.",
self.macro.acl_update,
sharing="app", app="search", **{"perms.read": "admin, nobody"}
)


if __name__ == "__main__":
try:
import unittest2 as unittest
except ImportError:
import unittest
unittest.main()
Loading