1#!/usr/bin/python 2# 3# Copyright: Ansible Project 4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 6from __future__ import absolute_import, division, print_function 7__metaclass__ = type 8 9ANSIBLE_METADATA = {'metadata_version': '1.1', 10 'status': ['preview'], 11 'supported_by': 'community'} 12 13DOCUMENTATION = """ 14--- 15module: onyx_config 16extends_documentation_fragment: onyx 17version_added: "2.5" 18author: "Alex Tabachnik (@atabachnik), Samer Deeb (@samerd)" 19short_description: Manage Mellanox ONYX configuration sections 20description: 21 - Mellanox ONYX configurations uses a simple block indent file syntax 22 for segmenting configuration into sections. This module provides 23 an implementation for working with ONYX configuration sections in 24 a deterministic way. 25options: 26 lines: 27 description: 28 - The ordered set of commands that should be configured in the 29 section. The commands must be the exact same commands as found 30 in the device running-config. Be sure to note the configuration 31 command syntax as some commands are automatically modified by the 32 device config parser. 33 aliases: ['commands'] 34 parents: 35 description: 36 - The ordered set of parents that uniquely identify the section 37 the commands should be checked against. If the parents argument 38 is omitted, the commands are checked against the set of top 39 level or global commands. 40 src: 41 description: 42 - Specifies the source path to the file that contains the configuration 43 or configuration template to load. The path to the source file can 44 either be the full path on the Ansible control host or a relative 45 path from the playbook or role root directory. This argument is mutually 46 exclusive with I(lines), I(parents). 47 before: 48 description: 49 - The ordered set of commands to push on to the command stack if 50 a change needs to be made. This allows the playbook designer 51 the opportunity to perform configuration commands prior to pushing 52 any changes without affecting how the set of commands are matched 53 against the system. 54 after: 55 description: 56 - The ordered set of commands to append to the end of the command 57 stack if a change needs to be made. Just like with I(before) this 58 allows the playbook designer to append a set of commands to be 59 executed after the command set. 60 match: 61 description: 62 - Instructs the module on the way to perform the matching of 63 the set of commands against the current device config. If 64 match is set to I(line), commands are matched line by line. If 65 match is set to I(strict), command lines are matched with respect 66 to position. If match is set to I(exact), command lines 67 must be an equal match. Finally, if match is set to I(none), the 68 module will not attempt to compare the source configuration with 69 the running configuration on the remote device. 70 default: line 71 choices: ['line', 'strict', 'exact', 'none'] 72 replace: 73 description: 74 - Instructs the module on the way to perform the configuration 75 on the device. If the replace argument is set to I(line) then 76 the modified lines are pushed to the device in configuration 77 mode. If the replace argument is set to I(block) then the entire 78 command block is pushed to the device in configuration mode if any 79 line is not correct 80 default: line 81 choices: ['line', 'block'] 82 backup: 83 description: 84 - This argument will cause the module to create a full backup of 85 the current C(running-config) from the remote device before any 86 changes are made. If the C(backup_options) value is not given, 87 the backup file is written to the C(backup) folder in the playbook 88 root directory. If the directory does not exist, it is created. 89 default: no 90 type: bool 91 config: 92 description: 93 - The C(config) argument allows the playbook designer to supply 94 the base configuration to be used to validate configuration 95 changes necessary. If this argument is provided, the module 96 will not download the running-config from the remote node. 97 save: 98 description: 99 - The C(save) argument instructs the module to save the running- 100 config to the startup-config at the conclusion of the module 101 running. If check mode is specified, this argument is ignored. 102 default: no 103 type: bool 104 backup_options: 105 description: 106 - This is a dict object containing configurable options related to backup file path. 107 The value of this option is read only when C(backup) is set to I(yes), if C(backup) is set 108 to I(no) this option will be silently ignored. 109 suboptions: 110 filename: 111 description: 112 - The filename to be used to store the backup configuration. If the the filename 113 is not given it will be generated based on the hostname, current time and date 114 in format defined by <hostname>_config.<current-date>@<current-time> 115 dir_path: 116 description: 117 - This option provides the path ending with directory name in which the backup 118 configuration file will be stored. If the directory does not exist it will be first 119 created and the filename is either the value of C(filename) or default filename 120 as described in C(filename) options description. If the path value is not given 121 in that case a I(backup) directory will be created in the current working directory 122 and backup configuration will be copied in C(filename) within I(backup) directory. 123 type: path 124 type: dict 125 version_added: "2.8" 126""" 127 128EXAMPLES = """ 129--- 130- onyx_config: 131 lines: 132 - snmp-server community 133 - snmp-server host 10.2.2.2 traps version 2c 134""" 135 136RETURN = """ 137updates: 138 description: The set of commands that will be pushed to the remote device 139 returned: always 140 type: list 141 sample: ['...', '...'] 142backup_path: 143 description: The full path to the backup file 144 returned: when backup is yes 145 type: str 146 sample: /playbooks/ansible/backup/onyx_config.2016-07-16@22:28:34 147""" 148 149from ansible.module_utils.basic import AnsibleModule 150from ansible.module_utils.network.common.config import NetworkConfig, dumps 151 152from ansible.module_utils.network.onyx.onyx import get_config 153from ansible.module_utils.network.onyx.onyx import load_config 154from ansible.module_utils.network.onyx.onyx import run_commands 155 156 157def get_candidate(module): 158 candidate = NetworkConfig(indent=1) 159 if module.params['src']: 160 candidate.load(module.params['src']) 161 elif module.params['lines']: 162 parents = module.params['parents'] or list() 163 candidate.add(module.params['lines'], parents=parents) 164 return candidate 165 166 167def run(module, result): 168 match = module.params['match'] 169 replace = module.params['replace'] 170 path = module.params['parents'] 171 172 candidate = get_candidate(module) 173 if match != 'none': 174 contents = module.params['config'] 175 if not contents: 176 contents = get_config(module) 177 config = NetworkConfig(indent=1, contents=contents) 178 configobjs = candidate.difference(config, path=path, match=match, 179 replace=replace) 180 181 else: 182 configobjs = candidate.items 183 184 total_commands = [] 185 if configobjs: 186 commands = dumps(configobjs, 'commands').split('\n') 187 188 if module.params['lines']: 189 if module.params['before']: 190 commands[:0] = module.params['before'] 191 192 if module.params['after']: 193 commands.extend(module.params['after']) 194 195 total_commands.extend(commands) 196 result['updates'] = total_commands 197 198 if module.params['save']: 199 total_commands.append('configuration write') 200 if total_commands: 201 result['changed'] = True 202 if not module.check_mode: 203 load_config(module, total_commands) 204 205 206def main(): 207 """ main entry point for module execution 208 """ 209 backup_spec = dict( 210 filename=dict(), 211 dir_path=dict(type='path') 212 ) 213 argument_spec = dict( 214 src=dict(type='path'), 215 216 lines=dict(aliases=['commands'], type='list'), 217 parents=dict(type='list'), 218 219 before=dict(type='list'), 220 after=dict(type='list'), 221 222 match=dict(default='line', choices=['line', 'strict', 'exact', 'none']), 223 replace=dict(default='line', choices=['line', 'block']), 224 225 config=dict(), 226 227 backup=dict(type='bool', default=False), 228 backup_options=dict(type='dict', options=backup_spec), 229 save=dict(type='bool', default=False), 230 ) 231 232 mutually_exclusive = [('lines', 'src'), 233 ('parents', 'src')] 234 235 required_if = [('match', 'strict', ['lines']), 236 ('match', 'exact', ['lines']), 237 ('replace', 'block', ['lines'])] 238 239 module = AnsibleModule(argument_spec=argument_spec, 240 mutually_exclusive=mutually_exclusive, 241 required_if=required_if, 242 supports_check_mode=True) 243 244 result = {'changed': False} 245 if module.params['backup']: 246 result['__backup__'] = get_config(module) 247 248 run(module, result) 249 250 module.exit_json(**result) 251 252 253if __name__ == '__main__': 254 main() 255