You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

228 lines
7.3 KiB

#
# Copyright 2021 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.
#
import json
import re
import warnings
from defusedxml import ElementTree as et
import splunktalib.rest as rest
class KVException(Exception):
pass
class KVAlreadyExists(KVException):
pass
class KVNotExists(KVException):
pass
class KVClient:
def __init__(self, splunkd_host, session_key):
warnings.warn(
"This class is deprecated. "
"Please see https://github.com/splunk/addonfactory-ta-library-python/issues/38",
DeprecationWarning,
stacklevel=2,
)
self._splunkd_host = splunkd_host
self._session_key = session_key
def create_collection(self, collection, app, owner="nobody"):
"""
:collection: collection name
:return: None if successful otherwise KV exception thrown
"""
assert collection
assert app
uri = self._get_config_endpoint(app, owner)
data = {"name": collection}
self._do_request(uri, "POST", data)
def list_collection(self, collection=None, app=None, owner="nobody"):
"""
:collection: collection name. When euqals "None", return all
collections in the system.
:return: a list containing the connection names if successful, throws
KVNotExists if no such colection or other exception if other error
happened
"""
uri = self._get_config_endpoint(app, owner, collection)
content = self._do_request(uri, method="GET")
m = re.search(r'xmlns="([^"]+)"', content)
path = "./entry/title"
if m:
ns = m.group(1)
path = "./{{{}}}entry/{{{}}}title".format(ns, ns)
collections = et.fromstring(content)
return [node.text for node in collections.iterfind(path)]
def delete_collection(self, collection, app, owner="nobody"):
"""
:collection: collection name to be deleted
:return: None if successful otherwise throw KVNotExists exception if
the collection doesn't exist in the system or other exception if other
error happened
"""
assert collection
uri = self._get_config_endpoint(app, owner, collection)
self._do_request(uri, method="DELETE")
def insert_collection_data(self, collection, data, app, owner="nobody"):
"""
:collection: collection name
:data: dict like key values to be inserted and attached to
this collection
:return: {"_key": "key_id"} when successful, clients can use this
key to do query/delete/update, throws KV exceptions when failed
"""
assert collection
assert data is not None
assert app
uri = self._get_data_endpoint(app, owner, collection)
key = self._do_request(uri, "POST", data, content_type="application/json")
return json.loads(key)
def delete_collection_data(self, collection, key_id, app, owner="nobody"):
"""
:collection: collection name
:key_id: key id returned when creation. If None, delete all data
associated with this collection
:return: None if successful otherwise throws KV exception
"""
assert collection
uri = self._get_data_endpoint(app, owner, collection, key_id)
self._do_request(uri, "DELETE", content_type="application/json")
def update_collection_data(self, collection, key_id, data, app, owner="nobody"):
"""
:collection: collection name
:key_id: key id returned when creation
:return: key id if successful otherwise throws KV exception
"""
assert collection
assert key_id
assert app
uri = self._get_data_endpoint(app, owner, collection, key_id)
k = self._do_request(uri, "POST", data, content_type="application/json")
return json.loads(k)
def get_collection_data(self, collection, key_id, app, owner="nobody"):
"""
:collection: collection name
:key_id: key id returned when creation. If None, get all data
associated with this collection
:return: when key_id is not None, return key values if
successful. when key_id is None, return a list of key values if
sucessful. Throws KV exception if failure
"""
assert collection
uri = self._get_data_endpoint(app, owner, collection, key_id)
k = self._do_request(uri, "GET")
return json.loads(k)
def _do_request(
self, uri, method, data=None, content_type="application/x-www-form-urlencoded"
):
headers = {"Content-Type": content_type}
resp = rest.splunkd_request(uri, self._session_key, method, headers, data)
if resp is None:
raise KVException("Failed uri={}, data={}".format(uri, data))
if resp.status_code in (200, 201):
return resp.text
elif resp.status_code == 409:
raise KVAlreadyExists("{}-{} already exists".format(uri, data))
elif resp.status_code == 404:
raise KVNotExists("{}-{} not exists".format(uri, data))
else:
raise KVException(
"Failed to {} {}, reason={}".format(method, uri, resp.reason)
)
def _get_config_endpoint(self, app, owner, collection=None):
uri = "{0}/servicesNS/{1}/{2}/storage/collections/config"
return self._do_get_endpoint(app, owner, collection, None, uri)
def _get_data_endpoint(self, app, owner, collection, key_id=None):
uri = "{0}/servicesNS/{1}/{2}/storage/collections/data"
return self._do_get_endpoint(app, owner, collection, key_id, uri)
def _do_get_endpoint(self, app, owner, collection, key_id, uri_template):
if not app:
app = "-"
if not owner:
owner = "-"
uri = uri_template.format(self._splunkd_host, owner, app)
if collection is not None:
uri += "/{}".format(collection)
if key_id is not None:
uri += "/{}".format(key_id)
return uri
def create_collection(kv_client, collection, appname):
warnings.warn(
"This function is deprecated. "
"Please see https://github.com/splunk/addonfactory-ta-library-python/issues/38",
DeprecationWarning,
stacklevel=2,
)
not_exists = False
try:
res = kv_client.list_collection(collection, appname)
except KVNotExists:
not_exists = True
except Exception:
not_exists = True
if not_exists or not res:
for i in range(3):
try:
kv_client.create_collection(collection, appname)
except KVAlreadyExists:
return
except Exception as e:
ex = e
else:
return
else:
raise ex