102102PATH_USERS = "authentication/users/"
103103PATH_RECEIVERS_STREAM = "receivers/stream"
104104PATH_RECEIVERS_SIMPLE = "receivers/simple"
105+ PATH_STORAGE_PASSWORDS = "storage/passwords"
105106
106107XNAMEF_ATOM = "{http://www.w3.org/2005/Atom}%s"
107108XNAME_ENTRY = XNAMEF_ATOM % "entry"
108109XNAME_CONTENT = XNAMEF_ATOM % "content"
109110
110111MATCH_ENTRY_CONTENT = "%s/%s/*" % (XNAME_ENTRY , XNAME_CONTENT )
111112
113+
112114class IllegalOperationException (Exception ):
113115 """Thrown when an operation is not possible on the Splunk instance that a
114116 :class:`Service` object is connected to."""
115117 pass
116118
119+
117120class IncomparableException (Exception ):
118121 """Thrown when trying to compare objects (using ``==``, ``<``, ``>``, and
119122 so on) of a type that doesn't support it."""
120123 pass
121124
125+
122126class AmbiguousReferenceException (ValueError ):
123127 """Thrown when the name used to fetch an entity matches more than one entity."""
124128 pass
125129
130+
126131class InvalidNameException (Exception ):
127132 """Thrown when the specified name contains characters that are not allowed
128133 in Splunk entity names."""
129134 pass
130135
136+
131137class NoSuchCapability (Exception ):
132138 """Thrown when the capability that has been referred to doesn't exist."""
133139 pass
134140
141+
135142class OperationError (Exception ):
136143 """Raised for a failed operation, such as a time out."""
137144 pass
138145
146+
139147class NotSupportedError (Exception ):
140148 """Raised for operations that are not supported on a given object."""
141149 pass
142150
151+
143152def _trailing (template , * targets ):
144153 """Substring of *template* following all *targets*.
145154
@@ -168,6 +177,7 @@ def _trailing(template, *targets):
168177 s = s [n + len (t ):]
169178 return s
170179
180+
171181# Filter the given state content record according to the given arg list.
172182def _filter_content (content , * args ):
173183 if len (args ) > 0 :
@@ -180,10 +190,12 @@ def _path(base, name):
180190 if not base .endswith ('/' ): base = base + '/'
181191 return base + name
182192
193+
183194# Load an atom record from the body of the given response
184195def _load_atom (response , match = None ):
185196 return data .load (response .body .read (), match )
186197
198+
187199# Load an array of atom entries from the body of the given response
188200def _load_atom_entries (response ):
189201 r = _load_atom (response )
@@ -203,10 +215,12 @@ def _load_atom_entries(response):
203215 if entries is None : return None
204216 return entries if isinstance (entries , list ) else [entries ]
205217
218+
206219# Load the sid from the body of the given response
207220def _load_sid (response ):
208221 return _load_atom (response ).response .sid
209222
223+
210224# Parse the given atom entry record into a generic entity state record
211225def _parse_atom_entry (entry ):
212226 title = entry .get ('title' , None )
@@ -233,6 +247,7 @@ def _parse_atom_entry(entry):
233247 'content' : content
234248 })
235249
250+
236251# Parse the metadata fields out of the given atom entry content record
237252def _parse_atom_metadata (content ):
238253 # Hoist access metadata
@@ -247,6 +262,7 @@ def _parse_atom_metadata(content):
247262
248263 return record ({'access' : access , 'fields' : fields })
249264
265+
250266# kwargs: scheme, host, port, app, owner, username, password
251267def connect (** kwargs ):
252268 """This function connects and logs in to a Splunk instance.
@@ -455,6 +471,14 @@ def modular_input_kinds(self):
455471 else :
456472 raise IllegalOperationException ("Modular inputs are not supported before Splunk version 5." )
457473
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+
458482 # kwargs: enable_lookups, reload_macros, parse_only, output_mode
459483 def parse (self , query , ** kwargs ):
460484 """Parses a search query and returns a semantic map of the search.
@@ -739,11 +763,8 @@ def post(self, path_segment="", owner=None, app=None, sharing=None, **query):
739763 if path_segment .startswith ('/' ):
740764 path = path_segment
741765 else :
742- path = self .service ._abspath (self .path + path_segment , owner = owner ,
743- app = app , sharing = sharing )
744- return self .service .post (path ,
745- owner = owner , app = app , sharing = sharing ,
746- ** query )
766+ path = self .service ._abspath (self .path + path_segment , owner = owner , app = app , sharing = sharing )
767+ return self .service .post (path , owner = owner , app = app , sharing = sharing , ** query )
747768
748769
749770# kwargs: path, app, owner, sharing, state
@@ -822,7 +843,8 @@ def __init__(self, service, path, **kwargs):
822843 Endpoint .__init__ (self , service , path )
823844 self ._state = None
824845 if not kwargs .get ('skip_refresh' , False ):
825- self .refresh (kwargs .get ('state' , None )) # "Prefresh"
846+ self .refresh (kwargs .get ('state' , None )) # "Prefresh"
847+ return
826848
827849 def __contains__ (self , item ):
828850 try :
@@ -1473,7 +1495,7 @@ def create(self, name, **params):
14731495 new_app = applications.create("my_fake_app")
14741496 """
14751497 if not isinstance (name , basestring ):
1476- raise InvalidNameException ("%s is not a valid name for an entity." % name )
1498+ raise InvalidNameException ("%s is not a valid name for an entity." % name )
14771499 if 'namespace' in params :
14781500 namespace = params .pop ('namespace' )
14791501 params ['owner' ] = namespace .owner
@@ -1648,6 +1670,70 @@ def __len__(self):
16481670 if not x .startswith ('eai' ) and x != 'disabled' ])
16491671
16501672
1673+ class StoragePassword (Entity ):
1674+ """This class contains a storage password.
1675+
1676+ """
1677+ def __init__ (self , service , path , ** kwargs ):
1678+ state = kwargs .get ('state' , None )
1679+ kwargs ['skip_refresh' ] = kwargs .get ('skip_refresh' , state is not None )
1680+ super (StoragePassword , self ).__init__ (service , path , ** kwargs )
1681+ self ._state = state
1682+
1683+ @property
1684+ def clear_password (self ):
1685+ return self .content .get ('clear_password' )
1686+
1687+ @property
1688+ def encrypted_password (self ):
1689+ return self .content .get ('encr_password' )
1690+
1691+ @property
1692+ def realm (self ):
1693+ return self .content .get ('realm' )
1694+
1695+ @property
1696+ def username (self ):
1697+ return self .content .get ('username' )
1698+
1699+
1700+ class StoragePasswords (Collection ):
1701+ """This class provides access to the storage passwords from this Splunk
1702+ instance. Retrieve this collection using :meth:`Service.storage_passwords`.
1703+
1704+ """
1705+ def __init__ (self , service ):
1706+ if service .namespace .owner == '-' or service .namespace .app == '-' :
1707+ raise ValueError ("StoragePasswords cannot have wildcards in namespace." )
1708+ super (StoragePasswords , self ).__init__ (service , PATH_STORAGE_PASSWORDS , item = StoragePassword )
1709+
1710+ def create (self , name , password ):
1711+ """ Creates or edits a storage password by *name*.
1712+
1713+ :param name: A name of the form "username" or "realm:username".
1714+ :type name: ``string``
1715+
1716+ :return: The :class:`StoragePassword` object created.
1717+
1718+ """
1719+ if not isinstance (name , basestring ):
1720+ raise ValueError ('Invalid name: %s' % repr (name ))
1721+
1722+ identity = name .split (':' , 1 )
1723+ realm , name = identity if len (identity ) == 2 else ('' , identity )
1724+
1725+ response = self .post (password = password , realm = realm , name = name )
1726+
1727+ if response .status != 201 :
1728+ raise ValueError ("Unexpected status code %s returned from creating a stanza" % response .status )
1729+
1730+ entries = _load_atom_entries (response )
1731+ state = _parse_atom_entry (entries [0 ])
1732+ storage_password = StoragePassword (self .service , self ._entity_path (state ), state = state , skip_refresh = True )
1733+
1734+ return storage_password
1735+
1736+
16511737class AlertGroup (Entity ):
16521738 """This class represents a group of fired alerts for a saved search. Access
16531739 it using the :meth:`alerts` property."""
0 commit comments