@ -69,6 +69,38 @@ options:
- State of the logging configuration .
- State of the logging configuration .
default : present
default : present
choices : [ ' present ' , ' absent ' ]
choices : [ ' present ' , ' absent ' ]
event :
description :
- Link / trunk enable / default interface configuration logging
choices : [ ' link-enable ' , ' link-default ' , ' trunk-enable ' , ' trunk-default ' ]
version_added : ' 2.8 '
message :
description :
- Add interface description to interface syslogs .
Does not work with version 6.0 images using nxapi as a transport .
choices : [ ' add-interface-description ' ]
version_added : ' 2.8 '
file_size :
description :
- Set logfile size
version_added : ' 2.8 '
facility_link_status :
description :
- Set logging facility ethpm link status .
Not idempotent with version 6.0 images .
choices : [ ' link-down-notif ' , ' link-down-error ' , ' link-up-notif ' , ' link-up-error ' ]
version_added : ' 2.8 '
timestamp :
description :
- Set logging timestamp format
choices : [ ' microseconds ' , ' milliseconds ' , ' seconds ' ]
version_added : ' 2.8 '
purge :
description :
- Remove any switch logging configuration that does not match what has been configured
type : bool
default : no
version_added : ' 2.8 '
extends_documentation_fragment : nxos
extends_documentation_fragment : nxos
"""
"""
@ -89,6 +121,12 @@ EXAMPLES = """
name : testfile
name : testfile
dest_level : 3
dest_level : 3
state : present
state : present
- name : Configure logging logfile with size
nxos_logging :
dest : logfile
name : testfile
dest_level : 3
file_size : 16384
- name : configure facility level logging
- name : configure facility level logging
nxos_logging :
nxos_logging :
facility : daemon
facility : daemon
@ -111,7 +149,26 @@ EXAMPLES = """
nxos_logging :
nxos_logging :
interface : mgmt0
interface : mgmt0
state : present
state : present
- name : Purge nxos_logging configuration not managed by this playbook
nxos_logging :
purge : true
- name : Configure logging timestamp
nxos_logging :
timestamp : milliseconds
state : present
- name : Configure logging facility ethpm link status
nxos_logging :
facility : ethpm
facility_link_status : link - up - notif
state : present
- name : Configure logging message ethernet description
nxos_logging :
message : add - interface - description
state : present
- name : Configure logging event link enable
nxos_logging :
event : link - enable
state : present
- name : Configure logging using aggregate
- name : Configure logging using aggregate
nxos_logging :
nxos_logging :
aggregate :
aggregate :
@ -133,11 +190,33 @@ commands:
"""
"""
import re
import re
import copy
from ansible . module_utils . network . nxos . nxos import get_config , load_config , run_commands
from ansible . module_utils . network . nxos . nxos import get_config , load_config , run_commands , save_module_context , read_module_context
from ansible . module_utils . network . nxos . nxos import nxos_argument_spec , check_args , normalize_interface
from ansible . module_utils . network . nxos . nxos import nxos_argument_spec , check_args , normalize_interface
from ansible . module_utils . basic import AnsibleModule
from ansible . module_utils . basic import AnsibleModule
STATIC_CLI = { ' link-enable ' : ' logging event link-status enable ' ,
' link-default ' : ' logging event link-status default ' ,
' trunk-enable ' : ' logging event trunk-status enable ' ,
' trunk-default ' : ' logging event trunk-status default ' ,
' microseconds ' : ' logging timestamp microseconds ' ,
' milliseconds ' : ' logging timestamp milliseconds ' ,
' seconds ' : ' logging timestamp seconds ' ,
' link-up-error ' : ' link-up error ' ,
' link-up-notif ' : ' link-up notif ' ,
' link-down-error ' : ' link-down error ' ,
' link-down-notif ' : ' link-down notif ' ,
' add-interface-description ' : ' logging message interface type ethernet description ' }
DEFAULT_LOGGING_LEVEL = { 0 : [ ] ,
1 : [ ] ,
2 : [ ] ,
3 : [ ' adjmgr ' , ' arp ' , ' icmpv6 ' , ' l2rib ' , ' netstack ' ] ,
4 : [ ] ,
5 : [ ] ,
6 : [ ] ,
7 : [ ] }
DEST_GROUP = [ ' console ' , ' logfile ' , ' module ' , ' monitor ' , ' server ' ]
DEST_GROUP = [ ' console ' , ' logfile ' , ' module ' , ' monitor ' , ' server ' ]
@ -152,8 +231,11 @@ def map_obj_to_commands(updates):
if state == ' absent ' and w in have :
if state == ' absent ' and w in have :
if w [ ' facility ' ] is not None :
if w [ ' facility ' ] is not None :
if not w [ ' dest ' ] :
if not w [ ' dest ' ] and not w [ ' facility_link_status ' ] and w [ ' facility ' ] not in DEFAULT_LOGGING_LEVEL [ int ( w [ ' facility_level ' ] ) ] :
commands . append ( ' no logging level {} ' . format ( w [ ' facility ' ] ) )
commands . append ( ' no logging level {} {} ' . format ( w [ ' facility ' ] , w [ ' facility_level ' ] ) )
if w [ ' facility_link_status ' ] and w [ ' facility ' ] in ( ' ethpm ' ) :
commands . append ( ' no logging level {} {} ' . format ( w [ ' facility ' ] , STATIC_CLI [ w [ ' facility_link_status ' ] ] ) )
if w [ ' name ' ] is not None :
if w [ ' name ' ] is not None :
commands . append ( ' no logging logfile ' )
commands . append ( ' no logging logfile ' )
@ -167,6 +249,15 @@ def map_obj_to_commands(updates):
if w [ ' interface ' ] :
if w [ ' interface ' ] :
commands . append ( ' no logging source-interface ' )
commands . append ( ' no logging source-interface ' )
if w [ ' event ' ] and w [ ' event ' ] in STATIC_CLI :
commands . append ( ' no ' + STATIC_CLI [ w [ ' event ' ] ] )
if w [ ' message ' ] and w [ ' message ' ] in STATIC_CLI :
commands . append ( ' no ' + STATIC_CLI [ w [ ' message ' ] ] )
if w [ ' timestamp ' ] and w [ ' timestamp ' ] in STATIC_CLI :
commands . append ( ' no ' + STATIC_CLI [ w [ ' timestamp ' ] ] )
if state == ' present ' and w not in have :
if state == ' present ' and w not in have :
if w [ ' facility ' ] is None :
if w [ ' facility ' ] is None :
if w [ ' dest ' ] :
if w [ ' dest ' ] :
@ -174,7 +265,12 @@ def map_obj_to_commands(updates):
commands . append ( ' logging {} {} ' . format ( w [ ' dest ' ] , w [ ' dest_level ' ] ) )
commands . append ( ' logging {} {} ' . format ( w [ ' dest ' ] , w [ ' dest_level ' ] ) )
elif w [ ' dest ' ] == ' logfile ' :
elif w [ ' dest ' ] == ' logfile ' :
commands . append ( ' logging logfile {} {} ' . format ( w [ ' name ' ] , w [ ' dest_level ' ] ) )
if w [ ' file_size ' ] :
commands . append ( ' logging logfile {} {} size {} ' . format (
w [ ' name ' ] , w [ ' dest_level ' ] , w [ ' file_size ' ] ) )
else :
commands . append ( ' logging logfile {} {} ' . format (
w [ ' name ' ] , w [ ' dest_level ' ] ) )
elif w [ ' dest ' ] == ' server ' :
elif w [ ' dest ' ] == ' server ' :
if w [ ' facility_level ' ] :
if w [ ' facility_level ' ] :
@ -209,12 +305,25 @@ def map_obj_to_commands(updates):
commands . append ( ' logging server {0} facility {1} ' . format ( w [ ' remote_server ' ] ,
commands . append ( ' logging server {0} facility {1} ' . format ( w [ ' remote_server ' ] ,
w [ ' facility ' ] ) )
w [ ' facility ' ] ) )
else :
else :
commands . append ( ' logging level {} {} ' . format ( w [ ' facility ' ] ,
if w [ ' facility_link_status ' ] :
w [ ' facility_level ' ] ) )
commands . append ( ' logging level {} {} ' . format (
w [ ' facility ' ] , STATIC_CLI [ w [ ' facility_link_status ' ] ] ) )
else :
commands . append ( ' logging level {} {} ' . format ( w [ ' facility ' ] ,
w [ ' facility_level ' ] ) )
if w [ ' interface ' ] :
if w [ ' interface ' ] :
commands . append ( ' logging source-interface {0} {1} ' . format ( * split_interface ( w [ ' interface ' ] ) ) )
commands . append ( ' logging source-interface {0} {1} ' . format ( * split_interface ( w [ ' interface ' ] ) ) )
if w [ ' event ' ] and w [ ' event ' ] in STATIC_CLI :
commands . append ( STATIC_CLI [ w [ ' event ' ] ] )
if w [ ' message ' ] and w [ ' message ' ] in STATIC_CLI :
commands . append ( STATIC_CLI [ w [ ' message ' ] ] )
if w [ ' timestamp ' ] and w [ ' timestamp ' ] in STATIC_CLI :
commands . append ( STATIC_CLI [ w [ ' timestamp ' ] ] )
return commands
return commands
@ -224,6 +333,75 @@ def split_interface(interface):
return match . group ( 1 ) , match . group ( 2 )
return match . group ( 1 ) , match . group ( 2 )
def parse_facility_link_status ( line , facility , status ) :
facility_link_status = None
if facility is not None :
match = re . search ( r ' logging level {} {} ( \ S+) ' . format ( facility , status ) , line , re . M )
if match :
facility_link_status = status + " - " + match . group ( 1 )
return facility_link_status
def parse_event_status ( line , event ) :
status = None
match = re . search ( r ' logging event {} ( \ S+) ' . format ( event + ' -status ' ) , line , re . M )
if match :
state = match . group ( 1 )
if state :
status = state
return status
def parse_event ( line ) :
event = None
match = re . search ( r ' logging event ( \ S+) ' , line , re . M )
if match :
state = match . group ( 1 )
if state == ' link-status ' :
event = ' link '
elif state == ' trunk-status ' :
event = ' trunk '
return event
def parse_message ( line ) :
message = None
match = re . search ( r ' logging message interface type ethernet description ' , line , re . M )
if match :
message = ' add-interface-description '
return message
def parse_file_size ( line , name , level ) :
file_size = None
match = re . search ( r ' logging logfile {} {} size ( \ S+) ' . format ( name , level ) , line , re . M )
if match :
file_size = match . group ( 1 )
if file_size == ' 8192 ' :
file_size = None
return file_size
def parse_timestamp ( line ) :
timestamp = None
match = re . search ( r ' logging timestamp ( \ S+) ' , line , re . M )
if match :
timestamp = match . group ( 1 )
return timestamp
def parse_name ( line , dest ) :
def parse_name ( line , dest ) :
name = None
name = None
@ -329,36 +507,76 @@ def parse_interface(line):
def map_config_to_obj ( module ) :
def map_config_to_obj ( module ) :
obj = [ ]
obj = [ ]
data = get_config ( module , flags = [ ' | section logging' ] )
data = get_config ( module , flags = [ ' all | section logging' ] )
for line in data . split ( ' \n ' ) :
for line in data . split ( ' \n ' ) :
if re . search ( r ' no ( \ S+) ' , line , re . M ) :
state = ' absent '
else :
state = ' present '
match = re . search ( r ' logging ( \ S+) ' , line , re . M )
match = re . search ( r ' logging ( \ S+) ' , line , re . M )
if state == ' present ' and match :
event_status = None
name = None
dest_level = None
dest = None
facility = None
remote_server = None
facility_link_status = None
file_size = None
facility_level = None
if match :
if match . group ( 1 ) in DEST_GROUP :
if match . group ( 1 ) in DEST_GROUP :
dest = match . group ( 1 )
dest = match . group ( 1 )
facility = None
name = parse_name ( line , dest )
remote_server = parse_remote_server ( line , dest )
dest_level = parse_dest_level ( line , dest , name )
if dest == ' server ' :
if dest == ' server ' :
facility = parse_facility ( line )
facility = parse_facility ( line )
facility_level = parse_facility_level ( line , facility , dest )
if dest == ' logfile ' :
file_size = parse_file_size ( line , name , dest_level )
elif match . group ( 1 ) == ' level ' :
elif match . group ( 1 ) == ' level ' :
match_facility = re . search ( r ' logging level ( \ S+) ' , line , re . M )
match_facility = re . search ( r ' logging level ( \ S+) ' , line , re . M )
facility = match_facility . group ( 1 )
facility = match_facility . group ( 1 )
dest = None
level = parse_facility_level ( line , facility , dest )
if level . isdigit ( ) :
facility_level = level
else :
facility_link_status = parse_facility_link_status ( line , facility , level )
elif match . group ( 1 ) == ' event ' and state == ' present ' :
event = parse_event ( line )
if event :
status = parse_event_status ( line , event )
if status :
event_status = event + ' - ' + status
else :
continue
else :
else :
dest = None
pass
facility = None
obj . append ( { ' dest ' : dest ,
obj . append ( { ' dest ' : dest ,
' remote_server ' : parse_remote_server ( line , dest ) ,
' remote_server ' : remote_server,
' use_vrf ' : parse_use_vrf ( line , dest ) ,
' use_vrf ' : parse_use_vrf ( line , dest ) ,
' name ' : parse_name ( line , dest ) ,
' name ' : name,
' facility ' : facility ,
' facility ' : facility ,
' dest_level ' : parse_dest_level ( line , dest , parse_name ( line , dest ) ) ,
' dest_level ' : dest_level ,
' facility_level ' : parse_facility_level ( line , facility , dest ) ,
' facility_level ' : facility_level ,
' interface ' : parse_interface ( line ) } )
' interface ' : parse_interface ( line ) ,
' facility_link_status ' : facility_link_status ,
' event ' : event_status ,
' file_size ' : file_size ,
' message ' : parse_message ( line ) ,
' timestamp ' : parse_timestamp ( line ) } )
cmd = [ { ' command ' : ' show logging | section enabled | section console ' , ' output ' : ' text ' } ,
cmd = [ { ' command ' : ' show logging | section enabled | section console ' , ' output ' : ' text ' } ,
{ ' command ' : ' show logging | section enabled | section monitor ' , ' output ' : ' text ' } ]
{ ' command ' : ' show logging | section enabled | section monitor ' , ' output ' : ' text ' } ]
@ -383,7 +601,12 @@ def map_config_to_obj(module):
' dest_level ' : dest_level ,
' dest_level ' : dest_level ,
' facility_level ' : None ,
' facility_level ' : None ,
' use_vrf ' : None ,
' use_vrf ' : None ,
' interface ' : None } )
' interface ' : None ,
' facility_link_status ' : None ,
' event ' : None ,
' file_size ' : None ,
' message ' : None ,
' timestamp ' : None } )
return obj
return obj
@ -399,7 +622,12 @@ def map_params_to_obj(module):
' facility ' : ' ' ,
' facility ' : ' ' ,
' dest_level ' : ' ' ,
' dest_level ' : ' ' ,
' facility_level ' : ' ' ,
' facility_level ' : ' ' ,
' interface ' : ' ' }
' interface ' : ' ' ,
' facility_link_status ' : None ,
' event ' : None ,
' file_size ' : None ,
' message ' : None ,
' timestamp ' : None }
for c in module . params [ ' aggregate ' ] :
for c in module . params [ ' aggregate ' ] :
d = c . copy ( )
d = c . copy ( )
@ -420,11 +648,15 @@ def map_params_to_obj(module):
if ' state ' not in d :
if ' state ' not in d :
d [ ' state ' ] = module . params [ ' state ' ]
d [ ' state ' ] = module . params [ ' state ' ]
if d [ ' file_size ' ] :
d [ ' file_size ' ] = str ( d [ ' file_size ' ] )
obj . append ( d )
obj . append ( d )
else :
else :
dest_level = None
dest_level = None
facility_level = None
facility_level = None
file_size = None
if module . params [ ' dest_level ' ] is not None :
if module . params [ ' dest_level ' ] is not None :
dest_level = str ( module . params [ ' dest_level ' ] )
dest_level = str ( module . params [ ' dest_level ' ] )
@ -432,6 +664,9 @@ def map_params_to_obj(module):
if module . params [ ' facility_level ' ] is not None :
if module . params [ ' facility_level ' ] is not None :
facility_level = str ( module . params [ ' facility_level ' ] )
facility_level = str ( module . params [ ' facility_level ' ] )
if module . params [ ' file_size ' ] is not None :
file_size = str ( module . params [ ' file_size ' ] )
obj . append ( {
obj . append ( {
' dest ' : module . params [ ' dest ' ] ,
' dest ' : module . params [ ' dest ' ] ,
' remote_server ' : module . params [ ' remote_server ' ] ,
' remote_server ' : module . params [ ' remote_server ' ] ,
@ -441,11 +676,44 @@ def map_params_to_obj(module):
' dest_level ' : dest_level ,
' dest_level ' : dest_level ,
' facility_level ' : facility_level ,
' facility_level ' : facility_level ,
' interface ' : normalize_interface ( module . params [ ' interface ' ] ) ,
' interface ' : normalize_interface ( module . params [ ' interface ' ] ) ,
' state ' : module . params [ ' state ' ]
' state ' : module . params [ ' state ' ] ,
' facility_link_status ' : module . params [ ' facility_link_status ' ] ,
' event ' : module . params [ ' event ' ] ,
' message ' : module . params [ ' message ' ] ,
' file_size ' : file_size ,
' timestamp ' : module . params [ ' timestamp ' ]
} )
} )
return obj
return obj
def merge_wants ( wants , want ) :
if not wants :
wants = list ( )
for w in want :
w = copy . copy ( w )
state = w [ ' state ' ]
del w [ ' state ' ]
if state == ' absent ' :
if w in wants :
wants . remove ( w )
elif w not in wants :
wants . append ( w )
return wants
def absent ( h ) :
h [ ' state ' ] = ' absent '
return h
def outliers ( haves , wants ) :
wants = list ( wants )
return [ absent ( h ) for h in haves if not ( h in wants or wants . append ( h ) ) ]
def main ( ) :
def main ( ) :
""" main entry point for module execution
""" main entry point for module execution
"""
"""
@ -458,8 +726,14 @@ def main():
dest_level = dict ( type = ' int ' , aliases = [ ' level ' ] ) ,
dest_level = dict ( type = ' int ' , aliases = [ ' level ' ] ) ,
facility_level = dict ( type = ' int ' ) ,
facility_level = dict ( type = ' int ' ) ,
interface = dict ( ) ,
interface = dict ( ) ,
facility_link_status = dict ( choices = [ ' link-down-notif ' , ' link-down-error ' , ' link-up-notif ' , ' link-up-error ' ] ) ,
event = dict ( choices = [ ' link-enable ' , ' link-default ' , ' trunk-enable ' , ' trunk-default ' ] ) ,
message = dict ( choices = [ ' add-interface-description ' ] ) ,
file_size = dict ( type = ' int ' ) ,
timestamp = dict ( choices = [ ' microseconds ' , ' milliseconds ' , ' seconds ' ] ) ,
state = dict ( default = ' present ' , choices = [ ' present ' , ' absent ' ] ) ,
state = dict ( default = ' present ' , choices = [ ' present ' , ' absent ' ] ) ,
aggregate = dict ( type = ' list ' )
aggregate = dict ( type = ' list ' ) ,
purge = dict ( default = False , type = ' bool ' )
)
)
argument_spec . update ( nxos_argument_spec )
argument_spec . update ( nxos_argument_spec )
@ -479,6 +753,7 @@ def main():
result [ ' warnings ' ] = warnings
result [ ' warnings ' ] = warnings
want = map_params_to_obj ( module )
want = map_params_to_obj ( module )
merged_wants = merge_wants ( read_module_context ( module ) , want )
have = map_config_to_obj ( module )
have = map_config_to_obj ( module )
commands = map_obj_to_commands ( ( want , have ) )
commands = map_obj_to_commands ( ( want , have ) )
@ -489,6 +764,16 @@ def main():
load_config ( module , commands )
load_config ( module , commands )
result [ ' changed ' ] = True
result [ ' changed ' ] = True
save_module_context ( module , merged_wants )
if module . params . get ( ' purge ' ) :
pcommands = map_obj_to_commands ( ( outliers ( have , merged_wants ) , have ) )
if pcommands :
if not module . check_mode :
load_config ( module , pcommands )
result [ ' changed ' ] = True
result [ ' commands ' ] + = pcommands
module . exit_json ( * * result )
module . exit_json ( * * result )