@ -39,46 +39,89 @@ options:
default : null
src :
description :
- Path to the configuration file to load .
- The C ( src ) argument specifies the path to the source config
file to load . The source config file can either be in
bracket format or set format . The source file can include
Jinja2 template variables .
required : no
default : null
rollback :
match :
description :
- Rollback the device configuration to the
revision specified . If the specified rollback revision does
not exist , then the module will produce an error and fail .
- NOTE THIS WILL CAUSE THE DEVICE TO REBOOT AUTOMATICALLY .
- The C ( match ) argument controls the method used to match
against the current active configuration . By default , the
desired config is matched against the active config and the
deltas are loaded . If the C ( match ) argument is set to C ( none )
the active configuration is ignored and the configuration is
always loaded .
required : false
default : null
update_config :
default : line
choices : [ ' line ' , ' none ' ]
update :
description :
- The C ( update ) argument controls the method used to update the
remote device configuration . This argument accepts two valid
options , C ( merge ) or C ( check ) . When C ( merge ) is specified , the
configuration is merged into the current active config . When
C ( check ) is specified , the module returns the set of updates
that would be applied to the active configuration .
required : false
default : merge
choices : [ ' merge ' , ' check ' ]
backup :
description :
- Should the configuration on the remote device be updated with the
calculated changes . When set to true , the configuration will be updated
and when set to false , the configuration will not be updated .
- The C ( backup ) argument will backup the current devices active
configuration to the Ansible control host prior to making any
changes . The backup file will be located in the backup folder
in the root of the playbook
required : false
default : true
default : fals e
choices : [ ' yes ' , ' no ' ]
backup_config :
comment :
description :
- Create a local backup copy of the current running configuration
prior to making any changes . The configuration file will be
stored in the backups folder in the root of the playbook or role .
- Allows a commit description to be specified to be included
when the configuration is committed . If the configuration is
not changed or committed , this argument is ignored .
required : false
default : ' configured by vyos_config '
config :
description :
- The C ( config ) argument specifies the base configuration to use
to compare against the desired configuration . If this value
is not specified , the module will automatically retrieve the
current active configuration from the remote device .
required : false
default : null
save :
description :
- The C ( save ) argument controls whether or not changes made
to the active configuration are saved to disk . This is
independent of committing the config . When set to True , the
active configuration is saved .
required : false
default : false
choices : [ ' yes ' , ' no ' ]
state :
description :
- The C ( state ) argument controls the existing state of the config
file on disk . When set to C ( present ) , the configuration should
exist on disk and when set to C ( absent ) the configuration file
is removed . This only applies to the startup configuration .
required : false
default : present
choices : [ ' present ' , ' absent ' ]
"""
RETURN = """
connected :
description : Boolean that specifies if the module connected to the device
returned : always
type : bool
sample : true
updates :
description : The list of configuration commands sent to the device
returned : always
type : list
sample : [ ' ... ' , ' ... ' ]
removed :
description : The list of configuration commands removed to avoid a load failure
returned : always
type : list
sample : [ ' ... ' , ' ... ' ]
"""
EXAMPLES = """
@ -99,16 +142,24 @@ vars:
- delete service dhcp - server
provider : " {{ cli }} "
- name : rollback config to revision 3
- name : backup and load from file
vyos_config :
rollback : 3
src : vyos . cfg
backup : yes
provider : " {{ cli }} "
"""
import re
from ansible . module_utils . network import Command , get_exception
from ansible . module_utils . netcfg import NetworkConfig , dumps
from ansible . module_utils . vyos import NetworkModule , NetworkError
from ansible . module_utils . vyos import vyos_argument_spec
from ansible . module_utils . vyos import load_config , load_candidate
DEFAULT_COMMENT = ' configured by vyos_config '
CONFIG_FILTERS = [
re . compile ( r ' set system login user \ S+ authentication encrypted-password ' )
]
def invoke ( name , * args , * * kwargs ) :
@ -116,6 +167,10 @@ def invoke(name, *args, **kwargs):
if func :
return func ( * args , * * kwargs )
def check_args ( module , warnings ) :
if module . params [ ' save ' ] and module . params [ ' update ' ] == ' check ' :
warnings . append ( ' The configuration will not be saved when update '
' is set to check ' )
def config_to_commands ( config ) :
set_format = config . startswith ( ' set ' ) or config . startswith ( ' delete ' )
@ -123,84 +178,158 @@ def config_to_commands(config):
if not set_format :
candidate = [ c . line for c in candidate . items ]
commands = list ( )
# this filters out less specific lines
for item in candidate :
for index , entry in enumerate ( commands ) :
if item . startswith ( entry ) :
del commands [ index ]
break
commands . append ( item )
else :
commands = str ( candidate ) . split ( ' \n ' )
return commands
def get_config ( module , result ) :
contents = module . params [ ' config ' ]
if not contents :
contents = module . config . get_config ( output = ' set ' ) . split ( ' \n ' )
else :
contents = config_to_commands ( contents )
def do_lines ( module , result ) :
commands = module . params [ ' lines ' ]
result . update ( load_config ( module , commands ) )
return contents
def get_candidate ( module ) :
contents = module . params [ ' src ' ] or module . params [ ' lines ' ]
def do_src ( module , result ) :
contents = module . params [ ' src ' ]
commands = config_to_commands ( contents )
result . update ( load_config ( module , commands ) )
if module . params [ ' lines ' ] :
contents = ' \n ' . join ( contents )
return config_to_commands ( contents )
def do_rollback ( module , result ) :
rollback = ' rollback %s ' % module . params [ ' rollback ' ]
prompt = re . compile ( ' \ [confirm \ ] ' )
cmd = Command ( rollback , prompt = prompt , response = ' y ' , is_reboot = True , delay = 1 )
def diff_config ( commands , config ) :
config = [ str ( c ) . replace ( " ' " , ' ' ) for c in config ]
try :
module . cli ( [ ' configure ' , cmd ] )
except NetworkError :
exc = get_exception ( )
cmds = [ str ( c ) for c in exc . kwargs . get ( ' commands ' , list ( ) ) ]
module . fail_json ( msg = str ( exc ) , commands = cmds )
updates = list ( )
visited = set ( )
for line in commands :
item = str ( line ) . replace ( " ' " , ' ' )
if not item . startswith ( ' set ' ) and not item . startswith ( ' delete ' ) :
raise ValueError ( ' line must start with either `set` or `delete` ' )
elif item . startswith ( ' set ' ) and item not in config :
updates . append ( line )
elif item . startswith ( ' delete ' ) :
if not config :
updates . append ( line )
else :
item = re . sub ( r ' delete ' , ' set ' , item )
for entry in config :
if entry . startswith ( item ) and line not in visited :
updates . append ( line )
visited . add ( line )
return list ( updates )
def sanitize_config ( config , result ) :
result [ ' removed ' ] = list ( )
for regex in CONFIG_FILTERS :
for index , line in enumerate ( list ( config ) ) :
if regex . search ( line ) :
result [ ' removed ' ] . append ( line )
del config [ index ]
def load_config ( module , commands , result ) :
comment = module . params [ ' comment ' ]
commit = not module . check_mode
save = module . params [ ' save ' ]
# sanitize loadable config to remove items that will fail
# remove items will be returned in the sanitized keyword
# in the result.
sanitize_config ( commands , result )
diff = module . config . load_config ( commands , commit = commit , comment = comment ,
save = save )
if diff :
result [ ' diff ' ] = dict ( prepared = diff )
result [ ' changed ' ] = True
def present ( module , result ) :
# get the current active config from the node or passed in via
# the config param
config = get_config ( module , result )
# create the candidate config object from the arguments
candidate = get_candidate ( module )
# create loadable config that includes only the configuration updates
updates = diff_config ( candidate , config )
result [ ' updates ' ] = updates
if module . params [ ' update ' ] != ' check ' :
load_config ( module , updates , result )
if result . get ( ' removed ' ) :
result [ ' warnings ' ] . append ( ' Some configuration commands where '
' removed, please see the removed key ' )
def absent ( module , result ) :
if not module . check_mode :
module . cli ( ' rm /config/config.boot ' )
result [ ' changed ' ] = True
def main ( ) :
argument_spec = dict (
lines = dict ( type = ' list ' ) ,
src = dict ( type = ' path ' ) ,
rollback = dict ( type = ' int ' ) ,
update_config = dict ( type = ' bool ' , default = False ) ,
backup_config = dict ( type = ' bool ' , default = False )
match = dict ( default = ' line ' , choices = [ ' line ' , ' none ' ] ) ,
update = dict ( default = ' merge ' , choices = [ ' merge ' , ' check ' ] ) ,
backup = dict ( default = False , type = ' bool ' ) ,
comment = dict ( default = DEFAULT_COMMENT ) ,
config = dict ( ) ,
save = dict ( default = False , type = ' bool ' ) ,
state = dict ( choices = [ ' present ' , ' absent ' ] , default = ' present ' )
)
argument_spec . update ( vyos_argument_spec )
mutually_exclusive = [ ( ' lines ' , ' rollback ' ) , ( ' lines ' , ' src ' ) ,
( ' src ' , ' rollback ' ) ]
mutually_exclusive = [ ( ' lines ' , ' src ' ) ]
module = NetworkModule ( argument_spec = argument_spec ,
connect_on_load = False ,
mutually_exclusive = mutually_exclusive ,
supports_check_mode = True )
module . check_mode = not module . params [ ' update_config ' ]
state = module . params [ ' state ' ]
result = dict ( changed = False , warnings = list ( ) )
warnings = list ( )
check_args ( module , warnings )
if module . params [ ' backup_config ' ] :
result [ ' __backup__ ' ] = module . cli ( ' show configuration ' ) [ 0 ]
result = dict ( changed = False , warnings = warnings )
if module . params [ ' lines ' ] :
do_lines ( module , result )
elif module . params [ ' src ' ] :
do_src ( module , result )
elif module . params [ ' rollback ' ] and not module . check_mode :
do_rollback ( module , result )
if ' filtered ' in result :
result [ ' warnings ' ] . append ( ' Some configuration commands where '
' filtered, please see the filtered key ' )
if module . params [ ' backup ' ] :
result [ ' __backup__ ' ] = module . config . get_config ( )
result [ ' connected ' ] = module . connected
try :
invoke ( state , module , result )
except NetworkError :
exc = get_exception ( )
module . fail_json ( msg = str ( exc ) , * * exc . kwargs )
module . exit_json ( * * result )