2020import os .path as op
2121import socket
2222import subprocess
23+ import json
2324from configparser import ConfigParser
2425from io import StringIO
2526from typing import List , Optional , Tuple , Union
2627
28+ from splunk .clilib .bundle_paths import make_splunkhome_path as msp
29+ from splunk .rest import simpleRequest
30+ from splunk import getSessionKey
31+
2732from .utils import is_true
2833
2934__all__ = [
3641 "get_conf_key_value" ,
3742 "get_conf_stanza" ,
3843 "get_conf_stanzas" ,
44+ "_get_conf_stanzas_from_splunk_api" ,
3945]
4046
4147ETC_LEAF = "etc"
5460]
5561
5662
57- def _splunk_home ():
58- return os .path .normpath (os .environ ["SPLUNK_HOME" ])
59-
60-
61- def _splunk_etc ():
62- try :
63- result = os .environ ["SPLUNK_ETC" ]
64- except KeyError :
65- result = op .join (_splunk_home (), ETC_LEAF )
66-
67- return os .path .normpath (result )
68-
69-
70- def _get_shared_storage () -> Optional [str ]:
71- """Get splunk shared storage name.
72-
73- Returns:
74- Splunk shared storage name.
75- """
76-
77- try :
78- state = get_conf_key_value ("server" , "pooling" , "state" , APP_SYSTEM )
79- storage = get_conf_key_value ("server" , "pooling" , "storage" , APP_SYSTEM )
80- except KeyError :
81- state = "disabled"
82- storage = None
83-
84- if state == "enabled" and storage :
85- return storage
86-
87- return None
88-
89-
90- # Verify path prefix and return true if both paths have drives
91- def _verify_path_prefix (path , start ):
92- path_drive = os .path .splitdrive (path )[0 ]
93- start_drive = os .path .splitdrive (start )[0 ]
94- return len (path_drive ) == len (start_drive )
95-
96-
9763def make_splunkhome_path (parts : Union [List , Tuple ]) -> str :
9864 """Construct absolute path by $SPLUNK_HOME and `parts`.
9965
@@ -111,53 +77,29 @@ def make_splunkhome_path(parts: Union[List, Tuple]) -> str:
11177 Raises:
11278 ValueError: Escape from intended parent directories.
11379 """
80+ return msp (parts )
11481
115- relpath = os .path .normpath (os .path .join (* parts ))
116-
117- basepath = None
118- shared_storage = _get_shared_storage ()
119- if shared_storage :
120- for candidate in on_shared_storage :
121- # SPL-100508 On windows if the path is missing the drive letter,
122- # construct fullpath manually and call relpath
123- if os .name == "nt" and not _verify_path_prefix (relpath , candidate ):
124- break
125-
126- if os .path .relpath (relpath , candidate )[0 :2 ] != ".." :
127- basepath = shared_storage
128- break
129-
130- if basepath is None :
131- etc_with_trailing_sep = os .path .join (ETC_LEAF , "" )
132- if relpath == ETC_LEAF or relpath .startswith (etc_with_trailing_sep ):
133- # Redirect $SPLUNK_HOME/etc to $SPLUNK_ETC.
134- basepath = _splunk_etc ()
135- # Remove leading etc (and path separator, if present). Note: when
136- # emitting $SPLUNK_ETC exactly, with no additional path parts, we
137- # set <relpath> to the empty string.
138- relpath = relpath [4 :]
139- else :
140- basepath = _splunk_home ()
141-
142- fullpath = os .path .normpath (os .path .join (basepath , relpath ))
143-
144- # Check that we haven't escaped from intended parent directories.
145- if os .path .relpath (fullpath , basepath )[0 :2 ] == ".." :
146- raise ValueError (
147- f'Illegal escape from parent directory "{ basepath } ": { fullpath } '
148- )
149- return fullpath
15082
151-
152- def get_splunk_host_info () -> Tuple :
83+ def get_splunk_host_info (
84+ use_btool : Optional [bool ] = False ,
85+ session_key : Optional [str ] = None
86+ ) -> Tuple :
15387 """Get splunk host info.
15488
15589 Returns:
15690 Tuple of (server_name, host_name).
15791 """
15892
159- server_name = get_conf_key_value ("server" , "general" , "serverName" , APP_SYSTEM )
93+ server_name = get_conf_key_value (
94+ "server" ,
95+ "general" ,
96+ "serverName" ,
97+ use_btool = use_btool ,
98+ session_key = session_key ,
99+ app_name = APP_SYSTEM
100+ )
160101 host_name = socket .gethostname ()
102+
161103 return server_name , host_name
162104
163105
@@ -175,21 +117,37 @@ def get_splunk_bin() -> str:
175117 return make_splunkhome_path (("bin" , splunk_bin ))
176118
177119
178- def get_splunkd_access_info () -> Tuple [str , str , int ]:
120+ def get_splunkd_access_info (
121+ use_btool : Optional [bool ] = False ,
122+ session_key : Optional [str ] = None
123+ ) -> Tuple [str , str , int ]:
179124 """Get splunkd server access info.
180125
181126 Returns:
182127 Tuple of (scheme, host, port).
183128 """
129+ enable_splunkd_ssl = get_conf_key_value (
130+ "server" ,
131+ "sslConfig" ,
132+ "enableSplunkdSSL" ,
133+ use_btool = use_btool ,
134+ session_key = session_key ,
135+ app_name = APP_SYSTEM
136+ )
184137
185- if is_true (
186- get_conf_key_value ("server" , "sslConfig" , "enableSplunkdSSL" , APP_SYSTEM )
187- ):
138+ if is_true (enable_splunkd_ssl ):
188139 scheme = "https"
189140 else :
190141 scheme = "http"
191142
192- host_port = get_conf_key_value ("web" , "settings" , "mgmtHostPort" , APP_SYSTEM )
143+ host_port = get_conf_key_value (
144+ "web" ,
145+ "settings" ,
146+ "mgmtHostPort" ,
147+ use_btool = use_btool ,
148+ session_key = session_key ,
149+ app_name = APP_SYSTEM
150+ )
193151 host_port = host_port .strip ()
194152 host_port_split_parts = host_port .split (":" )
195153 host = ":" .join (host_port_split_parts [:- 1 ])
@@ -203,14 +161,24 @@ def get_splunkd_access_info() -> Tuple[str, str, int]:
203161 return scheme , host , port
204162
205163
206- def get_scheme_from_hec_settings () -> str :
164+ def get_scheme_from_hec_settings (
165+ use_btool : Optional [bool ] = False ,
166+ session_key : Optional [str ] = None
167+ ) -> str :
207168 """Get scheme from HEC global settings.
208169
209170 Returns:
210171 scheme (str)
211172 """
212173 try :
213- ssl_enabled = get_conf_key_value ("inputs" , "http" , "enableSSL" , APP_HEC )
174+ ssl_enabled = get_conf_key_value (
175+ "inputs" ,
176+ "http" ,
177+ "enableSSL" ,
178+ use_btool = use_btool ,
179+ session_key = session_key ,
180+ app_name = APP_HEC
181+ )
214182 except KeyError :
215183 raise KeyError (
216184 "Cannot get enableSSL setting form conf: 'inputs' and stanza: '[http]'. "
@@ -237,20 +205,29 @@ def get_splunkd_uri() -> str:
237205 if os .environ .get ("SPLUNKD_URI" ):
238206 return os .environ ["SPLUNKD_URI" ]
239207
240- scheme , host , port = get_splunkd_access_info ()
208+ scheme , host , port = get_splunkd_access_info (use_btool = True )
241209 return f"{ scheme } ://{ host } :{ port } "
242210
243211
244212def get_conf_key_value (
245- conf_name : str , stanza : str , key : str , app_name : Optional [str ] = None
213+ conf_name : str ,
214+ stanza : str ,
215+ key : str ,
216+ use_btool : Optional [bool ] = False ,
217+ app_name : Optional [str ] = None ,
218+ session_key : Optional [str ] = None ,
219+ user : Optional [str ] = "nobody"
246220) -> Union [str , List , dict ]:
247221 """Get value of `key` of `stanza` in `conf_name`.
248222
249223 Arguments:
250224 conf_name: Config file.
251225 stanza: Stanza name.
252226 key: Key name.
227+ use_btool: If True, stanzas will be retrieved using cmd btool... otherwise using splunk API. Optional.
253228 app_name: Application name. Optional.
229+ session_key: If not provided solnlib will try to get it from splunk.getSessionKey(). Optional.
230+ user: used for set user context in API call. Optional.
254231
255232 Returns:
256233 Config value.
@@ -259,18 +236,43 @@ def get_conf_key_value(
259236 KeyError: If `stanza` or `key` doesn't exist.
260237 """
261238
262- stanzas = get_conf_stanzas (conf_name , app_name )
263- return stanzas [stanza ][key ]
239+ if use_btool :
240+ stanzas = get_conf_stanzas (conf_name , app_name )
241+ return stanzas [stanza ][key ]
242+
243+ if not app_name :
244+ raise KeyError ("app name must be specified if use_btool is True" )
245+
246+ stanzas = _get_conf_stanzas_from_splunk_api (
247+ conf_name ,
248+ app_name ,
249+ session_key = session_key ,
250+ user = user ,
251+ stanza = stanza
252+ )
253+
254+ stanza = stanzas .get ("entry" )[0 ].get ("content" )
255+ requested_key = stanza [key ]
256+ return requested_key
264257
265258
266259def get_conf_stanza (
267- conf_name : str , stanza : str , app_name : Optional [str ] = None
260+ conf_name : str ,
261+ stanza : str ,
262+ use_btool : Optional [bool ] = False ,
263+ app_name : Optional [str ] = None ,
264+ session_key : Optional [str ] = None ,
265+ user : Optional [str ] = "nobody"
268266) -> dict :
269267 """Get `stanza` in `conf_name`.
270268
271269 Arguments:
272270 conf_name: Config file.
273271 stanza: Stanza name.
272+ use_btool: If True, stanzas will be retrieved using cmd btool... otherwise using splunk API. Optional.
273+ app_name: Application name. Optional.
274+ session_key: If not provided solnlib will try to get it from splunk.getSessionKey(). Optional.
275+ user: used for set user context in API call. Optional.
274276 app_name: Application name. Optional.
275277
276278 Returns:
@@ -280,8 +282,23 @@ def get_conf_stanza(
280282 KeyError: If stanza doesn't exist.
281283 """
282284
283- stanzas = get_conf_stanzas (conf_name , app_name )
284- return stanzas [stanza ]
285+ if use_btool :
286+ stanzas = get_conf_stanzas (conf_name , app_name )
287+ return stanzas [stanza ] # uncomment after tests
288+
289+ if not app_name :
290+ raise KeyError ("app name must be specified if use_btool is True" )
291+
292+ stanzas = _get_conf_stanzas_from_splunk_api (
293+ conf_name ,
294+ app_name ,
295+ session_key = session_key ,
296+ user = user ,
297+ stanza = stanza
298+ )
299+
300+ stanza = stanzas .get ("entry" )[0 ].get ("content" )
301+ return stanza
285302
286303
287304def get_conf_stanzas (conf_name : str , app_name : Optional [str ] = None ) -> dict :
@@ -330,3 +347,41 @@ def get_conf_stanzas(conf_name: str, app_name: Optional[str] = None) -> dict:
330347 for section in parser .sections ():
331348 out [section ] = {item [0 ]: item [1 ] for item in parser .items (section , raw = True )}
332349 return out
350+
351+
352+ def _get_conf_stanzas_from_splunk_api (
353+ conf_name : str ,
354+ app_name : str ,
355+ session_key : Optional [str ] = None ,
356+ user : Optional [str ] = "nobody" ,
357+ stanza : Optional [str ] = None ,
358+ ) -> dict :
359+ """Get stanzas of `conf_name` using splunk API:
360+ /servicesNS/{user}/{app_name}/configs/conf-{conf_name}/{stanza}
361+
362+ Arguments:
363+ conf_name: Config file.
364+ app_name: Application name.
365+ session_key: Session key. Optional.
366+ user: Username. Optional.
367+ stanza: Stanza name. Optional.
368+
369+ Returns:
370+ json response.
371+ """
372+
373+ url = f"/servicesNS/{ user } /{ app_name } /configs/conf-{ conf_name } "
374+
375+ if stanza :
376+ url = url + "/" + stanza
377+
378+ if not session_key :
379+ session_key = getSessionKey ()
380+
381+ server_response , server_content = simpleRequest (
382+ url , sessionKey = session_key , getargs = {"output_mode" : "json" }, logme = True
383+ )
384+
385+ result = json .loads (server_content .decode ())
386+
387+ return result
0 commit comments