@@ -418,7 +418,14 @@ def restart(self, timeout=None):
418418 """
419419 result = self .get ("server/control/restart" )
420420 if timeout is None : return result
421- sleep (5 )
421+ start = datetime .now ()
422+ diff = timedelta (seconds = 10 )
423+ while datetime .now () - start < diff :
424+ try :
425+ self .login () # Has the server gone down yet?
426+ sleep (0.3 )
427+ except Exception :
428+ break # Server is down. Move on.
422429 start = datetime .now ()
423430 diff = timedelta (seconds = timeout )
424431 while datetime .now () - start < diff :
@@ -735,6 +742,17 @@ def _run_method(self, path_segment, **kwargs):
735742 return rec .content
736743
737744 def _proper_namespace (self , owner = None , app = None , sharing = None ):
745+ """Produce a namespace sans wildcards for use in entity requests.
746+
747+ This method handles the case of two entities with the same name in different namespaces
748+ showing up due to wildcards in the service's namespace. We replace the wildcards with the
749+ namespace of the entity we want.
750+ """
751+ :param owner :
752+ :param app :
753+ :param sharing :
754+ :return :
755+ """
738756 if owner is None and app is None and sharing is None and\
739757 (self.service.namespace.owner == '-' or self.service.namespace.app == '-'):
740758 # If no namespace is specified and there are wildcards in the service's namespace,
@@ -1303,13 +1321,11 @@ def list(self, count=None, **kwargs):
13031321 return list(self.iter(count=count, **kwargs))
13041322
13051323 def names(self, count=None, **kwargs):
1306- """Return a list over the names of entities in this collection.
1324+ """ Return a list of the names of all the entities in this collection .
13071325
1308- There is no laziness in this function. The entire collection
1309- is loaded at once and returned as a list. This function makes
1310- a single roundtrip to the server, plus at most two more if
1311- autologin is enabled. There is no caching: every call makes at
1312- least one round trip.
1326+ The entire list is loaded at once in a single roundtrip to the server ,
1327+ plus at most two more if autologin is enabled . There is no caching :
1328+ every call makes at least one round trip .
13131329
13141330 :param `count` : The maximum number of names to return (optional ).
13151331 :param `offset` : The offset of the first name to return (optional ).
@@ -1330,7 +1346,6 @@ class ConfigurationFile(Collection):
13301346 # Collection, since it is being created as the elements of a
13311347 # Configurations, which is a Collection subclass.
13321348 def __init__(self, service, path, **kwargs):
1333- #assert 'properties' not in path
13341349 Collection.__init__(self, service, path, item=Stanza)
13351350 self.name = kwargs['state']['title']
13361351
@@ -1347,9 +1362,9 @@ def __init__(self, service):
13471362 raise ValueError("Configurations cannot have wildcards in namespace.")
13481363
13491364 def __getitem__(self, key):
1350- # This has to be overridden because we get multiple values
1351- # back as a matter of course, unlike for most other endpoints
1352- # where multiple values means a name conflict .
1365+ # The configurations endpoint returns multiple entities when we ask for a single file.
1366+ # This screws up the default implementation of __getitem__ from Collection, which thinks
1367+ # that multiple entities means a name collision, so we have to override it here .
13531368 try:
13541369 response = self.get(key)
13551370 return ConfigurationFile(self.service, PATH_CONF % key, state={'title': key})
@@ -1409,6 +1424,9 @@ def submit(self, stanza):
14091424 return self
14101425
14111426 def __len__(self):
1427+ # The stanza endpoint returns all the keys at the same level in the XML as the eai information
1428+ # and 'disabled', so to get an accurate length, we have to filter those out and have just
1429+ # the stanza keys.
14121430 return len([x for x in self._state.content.keys()
14131431 if not x.startswith('eai') and x != 'disabled'])
14141432
@@ -2263,6 +2281,11 @@ def acknowledge(self):
22632281
22642282 @property
22652283 def alert_count(self):
2284+ """ Return the number of alerts fired by this saved search .
2285+
2286+ :return : The number of alerts fired by this saved search .
2287+ :rtype : integer
2288+ """
22662289 return int(self._state.content.get('triggered_alert_count', 0))
22672290
22682291 def dispatch(self, **kwargs):
@@ -2383,12 +2406,8 @@ def update(self, **kwargs):
23832406class User(Entity):
23842407 @property
23852408 def role_entities(self):
2386- role_names = self .content .roles
2387- roles = []
2388- for name in role_names :
2389- role = self .service .roles [name ]
2390- roles .append (role )
2391- return roles
2409+ """ Return a list of entities representing all the roles assigned to this user ."""
2410+ return [self.service.roles[name] for name in self.content.roles]
23922411
23932412# Splunk automatically lowercases new user names so we need to match that
23942413# behavior here to ensure that the subsequent member lookup works correctly.
@@ -2455,6 +2474,21 @@ def delete(self, name):
24552474
24562475class Role(Entity):
24572476 def grant(self, *capabilities_to_grant):
2477+ """ Grant additional capabilities to this role .
2478+
2479+ The capabilities are strings . You can get the complete list from
2480+ Service .capabilities , or from the / authorization / capabilities
2481+ endpoint in Splunk (or just look in splunkweb ).
2482+
2483+ ** Example ** ::
2484+
2485+ service = client .connect (...)
2486+ role = service .roles ['somerole ']
2487+ role .grant ('change_own_password ', 'search ')
2488+
2489+ :param capabilities_to_grant : Zero or more capabilities to grant this role .
2490+ :return : The Role entity .
2491+ """
24582492 possible_capabilities = self.service.capabilities
24592493 for capability in capabilities_to_grant:
24602494 if capability not in possible_capabilities:
@@ -2465,6 +2499,21 @@ def grant(self, *capabilities_to_grant):
24652499 return self
24662500
24672501 def revoke(self, *capabilities_to_revoke):
2502+ """ Revoke zero or more capabilities from this role .
2503+
2504+ The capabilities are strings . You can get the complete list from
2505+ Service .capabilities , or from the / authorization / capabilities
2506+ endpoint in Splunk (or just look in splunkweb ).
2507+
2508+ ** Example ** ::
2509+
2510+ service = client .connect (...)
2511+ role = service .roles ['somerole ']
2512+ role .revoke ('change_own_password ', 'search ')
2513+
2514+ :param capabilities_to_revoke : Zero or more capabilities to revoke from this role .
2515+ :return : The Role entity
2516+ """
24682517 possible_capabilities = self.service.capabilities
24692518 for capability in capabilities_to_revoke:
24702519 if capability not in possible_capabilities:
0 commit comments