%PDF- %PDF-
Direktori : /usr/lib/python2.7/site-packages/salt/modules/ |
Current File : //usr/lib/python2.7/site-packages/salt/modules/win_snmp.py |
# -*- coding: utf-8 -*- ''' Module for managing SNMP service settings on Windows servers. The Windows feature 'SNMP-Service' must be installed. ''' # Import Python libs from __future__ import absolute_import, unicode_literals, print_function import logging # Import Salt libs import salt.utils.platform from salt.exceptions import SaltInvocationError, CommandExecutionError # Import 3rd party libs from salt.ext import six _HKEY = 'HKLM' _SNMP_KEY = r'SYSTEM\CurrentControlSet\Services\SNMP\Parameters' _AGENT_KEY = r'{0}\RFC1156Agent'.format(_SNMP_KEY) _COMMUNITIES_KEY = r'{0}\ValidCommunities'.format(_SNMP_KEY) _SNMP_GPO_KEY = r'SOFTWARE\Policies\SNMP\Parameters' _COMMUNITIES_GPO_KEY = r'{0}\ValidCommunities'.format(_SNMP_GPO_KEY) _PERMISSION_TYPES = {'None': 1, 'Notify': 2, 'Read Only': 4, 'Read Write': 8, 'Read Create': 16} _SERVICE_TYPES = {'None': 0, 'Physical': 1, 'Datalink and subnetwork': 2, 'Internet': 4, 'End-to-end': 8, 'Applications': 64} _LOG = logging.getLogger(__name__) # Define the module's virtual name __virtualname__ = 'win_snmp' def __virtual__(): ''' Only works on Windows systems. ''' if not salt.utils.platform.is_windows(): return False, 'Module win_snmp: Requires Windows' if not __salt__['reg.key_exists'](_HKEY, _SNMP_KEY): return False, 'Module win_snmp: SNMP not installed' return __virtualname__ def _to_unicode(instr): ''' Converts from current users character encoding to unicode. When instr has a value of None, the return value of the function will also be None. ''' if instr is None or isinstance(instr, six.text_type): return instr else: return six.text_type(instr, 'utf8') def get_agent_service_types(): ''' Get the sysServices types that can be configured. Returns: list: A list of service types. CLI Example: .. code-block:: bash salt '*' win_snmp.get_agent_service_types ''' return list(_SERVICE_TYPES) def get_permission_types(): ''' Get the permission types that can be configured for communities. Returns: list: A list of permission types. CLI Example: .. code-block:: bash salt '*' win_snmp.get_permission_types ''' return list(_PERMISSION_TYPES) def get_agent_settings(): ''' Determine the value of the SNMP sysContact, sysLocation, and sysServices settings. Returns: dict: A dictionary of the agent settings. CLI Example: .. code-block:: bash salt '*' win_snmp.get_agent_settings ''' ret = dict() sorted_types = sorted(_SERVICE_TYPES.items(), key=lambda x: (-x[1], x[0])) ret['services'] = list() ret['contact'] = (__salt__['reg.read_value']( _HKEY, _AGENT_KEY, 'sysContact'))['vdata'] ret['location'] = (__salt__['reg.read_value']( _HKEY, _AGENT_KEY, 'sysLocation'))['vdata'] current_bitmask = (__salt__['reg.read_value']( _HKEY, _AGENT_KEY, 'sysServices'))['vdata'] if current_bitmask == 0: ret['services'].append(sorted_types[-1][0]) else: # sorted_types is sorted from greatest to least bitmask. for service, bitmask in sorted_types: if current_bitmask is not None and current_bitmask > 0: remaining_bitmask = current_bitmask - bitmask if remaining_bitmask >= 0: current_bitmask = remaining_bitmask ret['services'].append(service) else: break ret['services'] = sorted(ret['services']) return ret def set_agent_settings(contact=None, location=None, services=None): ''' Manage the SNMP sysContact, sysLocation, and sysServices settings. Args: contact (str, optional): The SNMP contact. location (str, optional): The SNMP location. services (list, optional): A list of selected services. The possible service names can be found via ``win_snmp.get_agent_service_types``. To disable all services pass a list of None, ie: ['None'] Returns: bool: True if successful, otherwise False CLI Example: .. code-block:: bash salt '*' win_snmp.set_agent_settings contact='Contact Name' location='Place' services="['Physical']" ''' if services is not None: # Filter services for unique items, and sort them for comparison # purposes. services = sorted(set(services)) # Validate the services. for service in services: if service not in _SERVICE_TYPES: message = ("Invalid service '{0}' specified. Valid services:" ' {1}').format(service, get_agent_service_types()) raise SaltInvocationError(message) if six.PY2: contact = _to_unicode(contact) location = _to_unicode(location) settings = {'contact': contact, 'location': location, 'services': services} current_settings = get_agent_settings() if settings == current_settings: _LOG.debug('Agent settings already contain the provided values.') return True if contact is not None: if contact != current_settings['contact']: __salt__['reg.set_value']( _HKEY, _AGENT_KEY, 'sysContact', contact, 'REG_SZ') if location is not None: if location != current_settings['location']: __salt__['reg.set_value']( _HKEY, _AGENT_KEY, 'sysLocation', location, 'REG_SZ') if services is not None: if set(services) != set(current_settings['services']): # Calculate the total value. Produces 0 if an empty list was provided, # corresponding to the None _SERVICE_TYPES value. vdata = sum(_SERVICE_TYPES[service] for service in services) _LOG.debug('Setting sysServices vdata to: %s', vdata) __salt__['reg.set_value']( _HKEY, _AGENT_KEY, 'sysServices', vdata, 'REG_DWORD') # Get the fields post-change so that we can verify tht all values # were modified successfully. Track the ones that weren't. new_settings = get_agent_settings() failed_settings = dict() for setting in settings: if settings[setting] is not None and \ settings[setting] != new_settings[setting]: failed_settings[setting] = settings[setting] if failed_settings: _LOG.error('Unable to configure agent settings: %s', failed_settings) return False _LOG.debug('Agent settings configured successfully: %s', settings.keys()) return True def get_auth_traps_enabled(): ''' Determine whether the host is configured to send authentication traps. Returns: bool: True if traps are enabled, otherwise False CLI Example: .. code-block:: bash salt '*' win_snmp.get_auth_traps_enabled ''' reg_ret = __salt__['reg.read_value']( _HKEY, _SNMP_KEY, 'EnableAuthenticationTraps') if reg_ret['vdata'] == '(value not set)': return False return bool(reg_ret['vdata'] or 0) def set_auth_traps_enabled(status=True): ''' Manage the sending of authentication traps. Args: status (bool): True to enable traps. False to disable. Returns: bool: True if successful, otherwise False CLI Example: .. code-block:: bash salt '*' win_snmp.set_auth_traps_enabled status='True' ''' vname = 'EnableAuthenticationTraps' current_status = get_auth_traps_enabled() if bool(status) == current_status: _LOG.debug('%s already contains the provided value.', vname) return True vdata = int(status) __salt__['reg.set_value'](_HKEY, _SNMP_KEY, vname, vdata, 'REG_DWORD') new_status = get_auth_traps_enabled() if status == new_status: _LOG.debug('Setting %s configured successfully: %s', vname, vdata) return True _LOG.error('Unable to configure %s with value: %s', vname, vdata) return False def get_community_names(): ''' Get the current accepted SNMP community names and their permissions. If community names are being managed by Group Policy, those values will be returned instead like this: .. code-block:: bash TestCommunity: Managed by GPO Community names managed normally will denote the permission instead: .. code-block:: bash TestCommunity: Read Only Returns: dict: A dictionary of community names and permissions. CLI Example: .. code-block:: bash salt '*' win_snmp.get_community_names ''' ret = dict() # Look in GPO settings first if __salt__['reg.key_exists'](_HKEY, _COMMUNITIES_GPO_KEY): _LOG.debug('Loading communities from Group Policy settings') current_values = __salt__['reg.list_values']( _HKEY, _COMMUNITIES_GPO_KEY, include_default=False) # GPO settings are different in that they do not designate permissions # They are a numbered list of communities like so: # # {1: "community 1", # 2: "community 2"} # # Denote that it is being managed by Group Policy. # # community 1: # Managed by GPO # community 2: # Managed by GPO if isinstance(current_values, list): for current_value in current_values: # Ignore error values if not isinstance(current_value, dict): continue ret[current_value['vdata']] = 'Managed by GPO' if not ret: _LOG.debug('Loading communities from SNMP settings') current_values = __salt__['reg.list_values']( _HKEY, _COMMUNITIES_KEY, include_default=False) # The communities are stored as the community name with a numeric # permission value. Like this (4 = Read Only): # # {"community 1": 4, # "community 2": 4} # # Convert the numeric value to the text equivalent, as present in the # Windows SNMP service GUI. # # community 1: # Read Only # community 2: # Read Only if isinstance(current_values, list): for current_value in current_values: # Ignore error values if not isinstance(current_value, dict): continue permissions = six.text_type() for permission_name in _PERMISSION_TYPES: if current_value['vdata'] == _PERMISSION_TYPES[permission_name]: permissions = permission_name break ret[current_value['vname']] = permissions if not ret: _LOG.debug('Unable to find existing communities.') return ret def set_community_names(communities): ''' Manage the SNMP accepted community names and their permissions. .. note:: Settings managed by Group Policy will always take precedence over those set using the SNMP interface. Therefore if this function finds Group Policy settings it will raise a CommandExecutionError Args: communities (dict): A dictionary of SNMP community names and permissions. The possible permissions can be found via ``win_snmp.get_permission_types``. Returns: bool: True if successful, otherwise False Raises: CommandExecutionError: If SNMP settings are being managed by Group Policy CLI Example: .. code-block:: bash salt '*' win_snmp.set_community_names communities="{'TestCommunity': 'Read Only'}' ''' values = dict() if __salt__['reg.key_exists'](_HKEY, _COMMUNITIES_GPO_KEY): _LOG.debug('Communities on this system are managed by Group Policy') raise CommandExecutionError( 'Communities on this system are managed by Group Policy') current_communities = get_community_names() if communities == current_communities: _LOG.debug('Communities already contain the provided values.') return True for vname in communities: if not communities[vname]: communities[vname] = 'None' try: vdata = _PERMISSION_TYPES[communities[vname]] except KeyError: message = ( "Invalid permission '{0}' specified. Valid permissions: " "{1}").format(communities[vname], _PERMISSION_TYPES.keys()) raise SaltInvocationError(message) values[vname] = vdata # Check current communities. for current_vname in current_communities: if current_vname in values: # Modify existing communities that have a different permission value. if current_communities[current_vname] != values[current_vname]: __salt__['reg.set_value']( _HKEY, _COMMUNITIES_KEY, current_vname, values[current_vname], 'REG_DWORD') else: # Remove current communities that weren't provided. __salt__['reg.delete_value']( _HKEY, _COMMUNITIES_KEY, current_vname) # Create any new communities. for vname in values: if vname not in current_communities: __salt__['reg.set_value']( _HKEY, _COMMUNITIES_KEY, vname, values[vname], 'REG_DWORD') # Get the fields post-change so that we can verify tht all values # were modified successfully. Track the ones that weren't. new_communities = get_community_names() failed_communities = dict() for new_vname in new_communities: if new_vname not in communities: failed_communities[new_vname] = None for vname in communities: if communities[vname] != new_communities[vname]: failed_communities[vname] = communities[vname] if failed_communities: _LOG.error('Unable to configure communities: %s', failed_communities) return False _LOG.debug('Communities configured successfully: %s', communities.keys()) return True