102
102
PATH_USERS = "authentication/users/"
103
103
PATH_RECEIVERS_STREAM = "receivers/stream"
104
104
PATH_RECEIVERS_SIMPLE = "receivers/simple"
105
+ PATH_STORAGE_PASSWORDS = "storage/passwords"
105
106
106
107
XNAMEF_ATOM = "{http://www.w3.org/2005/Atom}%s"
107
108
XNAME_ENTRY = XNAMEF_ATOM % "entry"
108
109
XNAME_CONTENT = XNAMEF_ATOM % "content"
109
110
110
111
MATCH_ENTRY_CONTENT = "%s/%s/*" % (XNAME_ENTRY , XNAME_CONTENT )
111
112
113
+
112
114
class IllegalOperationException (Exception ):
113
115
"""Thrown when an operation is not possible on the Splunk instance that a
114
116
:class:`Service` object is connected to."""
115
117
pass
116
118
119
+
117
120
class IncomparableException (Exception ):
118
121
"""Thrown when trying to compare objects (using ``==``, ``<``, ``>``, and
119
122
so on) of a type that doesn't support it."""
120
123
pass
121
124
125
+
122
126
class AmbiguousReferenceException (ValueError ):
123
127
"""Thrown when the name used to fetch an entity matches more than one entity."""
124
128
pass
125
129
130
+
126
131
class InvalidNameException (Exception ):
127
132
"""Thrown when the specified name contains characters that are not allowed
128
133
in Splunk entity names."""
129
134
pass
130
135
136
+
131
137
class NoSuchCapability (Exception ):
132
138
"""Thrown when the capability that has been referred to doesn't exist."""
133
139
pass
134
140
141
+
135
142
class OperationError (Exception ):
136
143
"""Raised for a failed operation, such as a time out."""
137
144
pass
138
145
146
+
139
147
class NotSupportedError (Exception ):
140
148
"""Raised for operations that are not supported on a given object."""
141
149
pass
142
150
151
+
143
152
def _trailing (template , * targets ):
144
153
"""Substring of *template* following all *targets*.
145
154
@@ -168,6 +177,7 @@ def _trailing(template, *targets):
168
177
s = s [n + len (t ):]
169
178
return s
170
179
180
+
171
181
# Filter the given state content record according to the given arg list.
172
182
def _filter_content (content , * args ):
173
183
if len (args ) > 0 :
@@ -180,10 +190,12 @@ def _path(base, name):
180
190
if not base .endswith ('/' ): base = base + '/'
181
191
return base + name
182
192
193
+
183
194
# Load an atom record from the body of the given response
184
195
def _load_atom (response , match = None ):
185
196
return data .load (response .body .read (), match )
186
197
198
+
187
199
# Load an array of atom entries from the body of the given response
188
200
def _load_atom_entries (response ):
189
201
r = _load_atom (response )
@@ -203,10 +215,12 @@ def _load_atom_entries(response):
203
215
if entries is None : return None
204
216
return entries if isinstance (entries , list ) else [entries ]
205
217
218
+
206
219
# Load the sid from the body of the given response
207
220
def _load_sid (response ):
208
221
return _load_atom (response ).response .sid
209
222
223
+
210
224
# Parse the given atom entry record into a generic entity state record
211
225
def _parse_atom_entry (entry ):
212
226
title = entry .get ('title' , None )
@@ -233,6 +247,7 @@ def _parse_atom_entry(entry):
233
247
'content' : content
234
248
})
235
249
250
+
236
251
# Parse the metadata fields out of the given atom entry content record
237
252
def _parse_atom_metadata (content ):
238
253
# Hoist access metadata
@@ -247,6 +262,7 @@ def _parse_atom_metadata(content):
247
262
248
263
return record ({'access' : access , 'fields' : fields })
249
264
265
+
250
266
# kwargs: scheme, host, port, app, owner, username, password
251
267
def connect (** kwargs ):
252
268
"""This function connects and logs in to a Splunk instance.
@@ -455,6 +471,14 @@ def modular_input_kinds(self):
455
471
else :
456
472
raise IllegalOperationException ("Modular inputs are not supported before Splunk version 5." )
457
473
474
+ @property
475
+ def storage_passwords (self ):
476
+ """Returns the collection of the modular input kinds on this Splunk instance.
477
+
478
+ :return: A :class:`ReadOnlyCollection` of :class:`ModularInputKind` entities.
479
+ """
480
+ return StoragePasswords (self )
481
+
458
482
# kwargs: enable_lookups, reload_macros, parse_only, output_mode
459
483
def parse (self , query , ** kwargs ):
460
484
"""Parses a search query and returns a semantic map of the search.
@@ -736,11 +760,8 @@ def post(self, path_segment="", owner=None, app=None, sharing=None, **query):
736
760
if path_segment .startswith ('/' ):
737
761
path = path_segment
738
762
else :
739
- path = self .service ._abspath (self .path + path_segment , owner = owner ,
740
- app = app , sharing = sharing )
741
- return self .service .post (path ,
742
- owner = owner , app = app , sharing = sharing ,
743
- ** query )
763
+ path = self .service ._abspath (self .path + path_segment , owner = owner , app = app , sharing = sharing )
764
+ return self .service .post (path , owner = owner , app = app , sharing = sharing , ** query )
744
765
745
766
746
767
# kwargs: path, app, owner, sharing, state
@@ -819,7 +840,8 @@ def __init__(self, service, path, **kwargs):
819
840
Endpoint .__init__ (self , service , path )
820
841
self ._state = None
821
842
if not kwargs .get ('skip_refresh' , False ):
822
- self .refresh (kwargs .get ('state' , None )) # "Prefresh"
843
+ self .refresh (kwargs .get ('state' , None )) # "Prefresh"
844
+ return
823
845
824
846
def __contains__ (self , item ):
825
847
try :
@@ -1273,6 +1295,7 @@ def _load_list(self, response):
1273
1295
self ._entity_path (state ),
1274
1296
state = state )
1275
1297
entities .append (entity )
1298
+
1276
1299
return entities
1277
1300
1278
1301
def itemmeta (self ):
@@ -1475,7 +1498,7 @@ def create(self, name, **params):
1475
1498
new_app = applications.create("my_fake_app")
1476
1499
"""
1477
1500
if not isinstance (name , basestring ):
1478
- raise InvalidNameException ("%s is not a valid name for an entity." % name )
1501
+ raise InvalidNameException ("%s is not a valid name for an entity." % name )
1479
1502
if 'namespace' in params :
1480
1503
namespace = params .pop ('namespace' )
1481
1504
params ['owner' ] = namespace .owner
@@ -1701,6 +1724,102 @@ def __len__(self):
1701
1724
if not x .startswith ('eai' ) and x != 'disabled' ])
1702
1725
1703
1726
1727
+ class StoragePassword (Entity ):
1728
+ """This class contains a storage password.
1729
+ """
1730
+ def __init__ (self , service , path , ** kwargs ):
1731
+ state = kwargs .get ('state' , None )
1732
+ kwargs ['skip_refresh' ] = kwargs .get ('skip_refresh' , state is not None )
1733
+ super (StoragePassword , self ).__init__ (service , path , ** kwargs )
1734
+ self ._state = state
1735
+
1736
+ @property
1737
+ def clear_password (self ):
1738
+ return self .content .get ('clear_password' )
1739
+
1740
+ @property
1741
+ def encrypted_password (self ):
1742
+ return self .content .get ('encr_password' )
1743
+
1744
+ @property
1745
+ def realm (self ):
1746
+ return self .content .get ('realm' )
1747
+
1748
+ @property
1749
+ def username (self ):
1750
+ return self .content .get ('username' )
1751
+
1752
+
1753
+ class StoragePasswords (Collection ):
1754
+ """This class provides access to the storage passwords from this Splunk
1755
+ instance. Retrieve this collection using :meth:`Service.storage_passwords`.
1756
+ """
1757
+ def __init__ (self , service ):
1758
+ if service .namespace .owner == '-' or service .namespace .app == '-' :
1759
+ raise ValueError ("StoragePasswords cannot have wildcards in namespace." )
1760
+ super (StoragePasswords , self ).__init__ (service , PATH_STORAGE_PASSWORDS , item = StoragePassword )
1761
+
1762
+ def create (self , password , username , realm = None ):
1763
+ """ Creates a storage password.
1764
+
1765
+ A `StoragePassword` can be identified by <username>, or by <realm>:<username> if the
1766
+ optional realm parameter is also provided.
1767
+
1768
+ :param password: The password for the credentials - this is the only part of the credentials that will be stored securely.
1769
+ :type name: ``string``
1770
+ :param username: The username for the credentials.
1771
+ :type name: ``string``
1772
+ :param realm: The credential realm. (optional)
1773
+ :type name: ``string``
1774
+
1775
+ :return: The :class:`StoragePassword` object created.
1776
+ """
1777
+ if not isinstance (username , basestring ):
1778
+ raise ValueError ("Invalid name: %s" % repr (username ))
1779
+
1780
+ if realm is None :
1781
+ response = self .post (password = password , name = username )
1782
+ else :
1783
+ response = self .post (password = password , realm = realm , name = username )
1784
+
1785
+ if response .status != 201 :
1786
+ raise ValueError ("Unexpected status code %s returned from creating a stanza" % response .status )
1787
+
1788
+ entries = _load_atom_entries (response )
1789
+ state = _parse_atom_entry (entries [0 ])
1790
+ storage_password = StoragePassword (self .service , self ._entity_path (state ), state = state , skip_refresh = True )
1791
+
1792
+ return storage_password
1793
+
1794
+ def delete (self , username , realm = None ):
1795
+ """Delete a storage password by username and/or realm.
1796
+
1797
+ The identifier can be passed in through the username parameter as
1798
+ <username> or <realm>:<username>, but the preferred way is by
1799
+ passing in the username and realm parameters.
1800
+
1801
+ :param username: The username for the credentials, or <realm>:<username> if the realm parameter is omitted.
1802
+ :type name: ``string``
1803
+ :param realm: The credential realm. (optional)
1804
+ :type name: ``string``
1805
+ :return: The `StoragePassword` collection.
1806
+ :rtype: ``self``
1807
+ """
1808
+ if realm is None :
1809
+ # This case makes the username optional, so
1810
+ # the full name can be passed in as realm.
1811
+ # Assume it's already encoded.
1812
+ name = username
1813
+ else :
1814
+ # Encode each component separately
1815
+ name = UrlEncoded (realm , encode_slash = True ) + ":" + UrlEncoded (username , encode_slash = True )
1816
+
1817
+ # Append the : expected at the end of the name
1818
+ if name [- 1 ] is not ":" :
1819
+ name = name + ":"
1820
+ return Collection .delete (self , name )
1821
+
1822
+
1704
1823
class AlertGroup (Entity ):
1705
1824
"""This class represents a group of fired alerts for a saved search. Access
1706
1825
it using the :meth:`alerts` property."""
0 commit comments