@ -20,7 +20,7 @@ DOCUMENTATION = """
- - -
module : ops_config
version_added : " 2.1 "
author : " Peter s prygada (@privateip)"
author : " Peter S prygada (@privateip)"
short_description : Manage OpenSwitch configuration using CLI
description :
- OpenSwitch configurations use a simple block indent file syntax
@ -36,7 +36,8 @@ options:
in the device running - config . Be sure to note the configuration
command syntax as some commands are automatically modified by the
device config parser .
required : true
required : false
default : null
parents :
description :
- The ordered set of parents that uniquely identify the section
@ -45,6 +46,17 @@ options:
level or global commands .
required : false
default : null
src :
description :
- The I ( src ) argument provides a path to the configuration file
to load into the remote system . The path can either be a full
system path to the configuration file if the value starts with /
or relative to the root of the implemented role or playbook .
This arugment is mutually exclusive with the I ( lines ) and
I ( parents ) arguments .
required : false
default : null
version_added : " 2.2 "
before :
description :
- The ordered set of commands to push on to the command stack if
@ -72,7 +84,7 @@ options:
must be an equal match .
required : false
default : line
choices : [ ' line ' , ' strict ' , ' exact ' ]
choices : [ ' line ' , ' strict ' , ' exact ' , ' none ' ]
replace :
description :
- Instructs the module on the way to perform the configuration
@ -90,9 +102,25 @@ options:
current devices running - config . When set to true , this will
cause the module to push the contents of I ( src ) into the device
without first checking if already configured .
- Note this argument should be considered deprecated . To achieve
the equivalent , set the match argument to none . This argument
will be removed in a future release .
required : false
default : false
choices : [ ' true ' , ' false ' ]
choices : [ ' yes ' , ' no ' ]
update :
description :
- The I ( update ) argument controls how the configuration statements
are processed on the remote device . Valid choices for the I ( update )
argument are I ( merge ) I ( replace ) and I ( check ) . When the argument is
set to I ( merge ) , the configuration changes are merged with the current
device running configuration . When the argument is set to I ( check )
the configuration updates are determined but not actually configured
on the remote device .
required : false
default : merge
choices : [ ' merge ' , ' check ' ]
version_added : " 2.2 "
config :
description :
- The module , by default , will connect to the remote device and
@ -104,20 +132,55 @@ options:
config for comparison .
required : false
default : null
save :
description :
- The C ( save ) argument instructs the module to save the running -
config to the startup - config at the conclusion of the module
running . If check mode is specified , this argument is ignored .
required : false
default : no
choices : [ ' yes ' , ' no ' ]
version_added : " 2.2 "
state :
description :
- This argument specifies whether or not the running - config is
present on the remote device . When set to I ( absent ) the
running - config on the remote device is erased .
required : false
default : no
choices : [ ' yes ' , ' no ' ]
version_added : " 2.2 "
"""
EXAMPLES = """
# Note: examples below use the following provider dict to handle
# transport and authentication to the node.
vars :
cli :
host : " {{ inventory_hostname }} "
username : netop
password : netop
- name : configure hostname over cli
ops_config :
lines :
- " hostname {{ inventory_hostname }} "
lines :
- " hostname {{ inventory_hostname }} "
provider : " {{ cli }} "
- name : configure vlan 10 over cli
ops_config :
lines :
- no shutdown
parents :
- vlan 10
lines :
- no shutdown
parents :
- vlan 10
provider : " {{ cli }} "
- name : load config from file
ops_config :
src : ops01 . cfg
backup : yes
provider : " {{ cli }} "
"""
RETURN = """
@ -126,122 +189,166 @@ updates:
returned : always
type : list
sample : [ ' ... ' , ' ... ' ]
backup_path :
description : The full path to the backup file
returned : when backup is yes
type : path
sample : / playbooks / ansible / backup / ios_config .2016 - 07 - 16 @ 22 : 28 : 34
responses :
description : The set of responses from issuing the commands on the device
retured : when not check_mode
retur n ed: when not check_mode
type : list
sample : [ ' ... ' , ' ... ' ]
"""
import re
import itertools
def get_config ( module ) :
config = module . params [ ' config ' ] or dict ( )
if not config and not module . params [ ' force ' ] :
config = module . config
return config
def build_candidate ( lines , parents , config , strategy ) :
candidate = list ( )
if strategy == ' strict ' :
for index , cmd in enumerate ( lines ) :
try :
if cmd != config [ index ] :
candidate . append ( cmd )
except IndexError :
candidate . append ( cmd )
elif strategy == ' exact ' :
if len ( lines ) != len ( config ) :
candidate = list ( lines )
else :
for cmd , cfg in itertools . izip ( lines , config ) :
if cmd != cfg :
candidate = list ( lines )
break
else :
for cmd in lines :
if cmd not in config :
candidate . append ( cmd )
from ansible . module_utils . basic import get_exception
from ansible . module_utils . openswitch import NetworkModule , NetworkError
from ansible . module_utils . netcfg import NetworkConfig , dumps
from ansible . module_utils . netcli import Command
def invoke ( name , * args , * * kwargs ) :
func = globals ( ) . get ( name )
if func :
return func ( * args , * * kwargs )
def check_args ( module , warnings ) :
if module . params [ ' parents ' ] :
if not module . params [ ' lines ' ] or module . params [ ' src ' ] :
warnings . append ( ' ignoring unnecessary argument parents ' )
if module . params [ ' force ' ] :
warnings . append ( ' The force argument is deprecated, please use '
' match=none instead. This argument will be '
' removed in the future ' )
def get_config ( module , result ) :
contents = module . params [ ' config ' ]
if not contents :
contents = module . config . get_config ( )
return NetworkConfig ( indent = 4 , contents = contents )
def get_candidate ( module ) :
candidate = NetworkConfig ( indent = 4 )
if module . params [ ' src ' ] :
candidate . load ( module . params [ ' src ' ] )
elif module . params [ ' lines ' ] :
parents = module . params [ ' parents ' ] or list ( )
candidate . add ( module . params [ ' lines ' ] , parents = parents )
return candidate
def load_backup ( module ) :
try :
module . cli ( [ ' exit ' , ' config replace flash:/ansible-rollback force ' ] )
except NetworkError :
module . fail_json ( msg = ' unable to rollback configuration ' )
def backup_config ( module ) :
cmd = ' copy running-config flash:/ansible-rollback '
cmd = Command ( cmd , prompt = re . compile ( ' \ ? $ ' ) , response = ' \n ' )
module . cli ( cmd )
def load_config ( module , commands , result ) :
if not module . check_mode and module . params [ ' update ' ] != ' check ' :
module . config ( commands )
result [ ' changed ' ] = module . params [ ' update ' ] != ' check '
result [ ' updates ' ] = commands . split ( ' \n ' )
def present ( module , result ) :
match = module . params [ ' match ' ]
replace = module . params [ ' replace ' ]
candidate = get_candidate ( module )
if match != ' none ' :
config = get_config ( module , result )
configobjs = candidate . difference ( config , match = match , replace = replace )
else :
config = None
configobjs = candidate . items
if configobjs :
commands = dumps ( configobjs , ' commands ' )
if module . params [ ' before ' ] :
commands [ : 0 ] = module . params [ ' before ' ]
if module . params [ ' after ' ] :
commands . extend ( module . params [ ' after ' ] )
# send the configuration commands to the device and merge
# them with the current running config
load_config ( module , commands , result )
if module . params [ ' save ' ] and not module . check_mode :
module . config . save_config ( )
def absent ( module , result ) :
if not module . check_mode :
module . cli ( ' erase startup-config ' )
result [ ' changed ' ] = True
def main ( ) :
argument_spec = dict (
lines = dict ( aliases = [ ' commands ' ] , required = True , type = ' list ' ) ,
lines = dict ( aliases = [ ' commands ' ] , type = ' list ' ) ,
parents = dict ( type = ' list ' ) ,
src = dict ( type = ' path ' ) ,
before = dict ( type = ' list ' ) ,
after = dict ( type = ' list ' ) ,
match = dict ( default = ' line ' , choices = [ ' line ' , ' strict ' , ' exact ' ] ) ,
match = dict ( default = ' line ' , choices = [ ' line ' , ' strict ' , ' exact ' , ' none ' ] ) ,
replace = dict ( default = ' line ' , choices = [ ' line ' , ' block ' ] ) ,
# this argument is deprecated in favor of setting match: none
# it will be removed in a future version
force = dict ( default = False , type = ' bool ' ) ,
config = dict ( ) ,
transport = dict ( default = ' cli ' , choices = [ ' cli ' ] )
)
module = get_module ( argument_spec = argument_spec ,
supports_check_mode = True )
update = dict ( choices = [ ' merge ' , ' check ' ] , default = ' merge ' ) ,
backup = dict ( type = ' bool ' , default = False ) ,
lines = module . params [ ' lines ' ]
parents = module . params [ ' parents ' ] or list ( )
config = dict ( ) ,
default = dict ( type = ' bool ' , default = False ) ,
before = module . params [ ' before ' ]
after = module . params [ ' after ' ]
save = dict ( type = ' bool ' , default = False ) ,
match = module . params [ ' match ' ]
replace = module . params [ ' replace ' ]
state = dict ( choices = [ ' present ' , ' absent ' ] , default = ' present ' ) ,
contents = get_config ( module )
config = module . parse_config ( contents )
# ops_config is only supported over Cli transport so force
# the value of transport to be cli
transport = dict ( default = ' cli ' , choices = [ ' cli ' ] )
)
if parents :
for parent in parents :
for item in config :
if item . text == parent :
config = item
mutually_exclusive = [ ( ' lines ' , ' src ' ) ]
try :
children = [ c . text for c in config . children ]
except AttributeError :
children = [ c . text for c in config ]
module = NetworkModule ( argument_spec = argument_spec ,
connect_on_load = False ,
mutually_exclusive = mutually_exclusive ,
supports_check_mode = True )
else :
children = [ c . text for c in config if not c . parents ]
state = module . params [ ' state ' ]
result = dict ( changed = False )
if module . params [ ' force ' ] is True :
module . params [ ' match ' ] = ' none '
candidate = build_candidate ( lines , parents , children , match )
warnings = list ( )
check_args ( module , warnings )
if candidate :
if replace == ' line ' :
candidate [ : 0 ] = parents
else :
candidate = list ( parents )
candidate . extend ( lines )
result = dict ( changed = False , warnings = warnings )
if before :
candidate [ : 0 ] = before
if module . params [ ' backup ' ] :
result [ ' __backup__ ' ] = module . config . get_config ( )
if after :
candidate . extend ( after )
try :
invoke ( state , module , result )
except NetworkError :
exc = get_exception ( )
module . fail_json ( msg = str ( exc ) )
if not module . check_mode :
response = module . configure ( candidate )
result [ ' responses ' ] = response
result [ ' changed ' ] = True
module . exit_json ( * * result )
result [ ' updates ' ] = candidate
return module . exit_json ( * * result )
from ansible . module_utils . basic import *
from ansible . module_utils . shell import *
from ansible . module_utils . netcfg import *
from ansible . module_utils . openswitch import *
if __name__ == ' __main__ ' :
main ( )