diff --git a/lib/ansible/module_utils/vyos.py b/lib/ansible/module_utils/vyos.py new file mode 100644 index 00000000000..1ae74b0900e --- /dev/null +++ b/lib/ansible/module_utils/vyos.py @@ -0,0 +1,154 @@ +# +# (c) 2015 Peter Sprygada, +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . +# + +import re + +from ansible.module_utils.network import NetworkError, get_module, get_exception +from ansible.module_utils.network import register_transport, to_list +from ansible.module_utils.network import Command, NetCli +from ansible.module_utils.shell import Shell, ShellError, HAS_PARAMIKO + +DEFAULT_COMMENT = 'configured by vyos_config' + +def argument_spec(): + return dict( + config=dict(), + comment=dict(default=DEFAULT_COMMENT), + save=dict(type='bool') + ) +vyos_argument_spec = argument_spec() + +get_config = lambda x: x.params['config'] or x.config.get_config() + +def diff_config(candidate, config): + updates = set() + config = [str(c).replace("'", '') for c in config] + + for line in candidate: + 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.add(line) + + elif item.startswith('delete'): + if not config: + updates.add(line) + else: + item = re.sub(r'delete', 'set', item) + for entry in config: + if entry.startswith(item): + updates.add(line) + + return list(updates) + +def load_config(module, candidate): + config = get_config(module).split('\n') + updates = diff_config(candidate, config) + + comment = module.params['comment'] + save = module.params['save'] + + result = dict(changed=False) + + if updates: + diff = module.config.load_config(updates) + if diff: + result['diff'] = dict(prepared=diff) + + result['changed'] = True + result['updates'] = updates + + if not module.check_mode: + module.config.commit_config(comment=comment) + if save: + module.config.save_config() + else: + module.config.abort_config() + + # exit from config mode + module.cli('exit') + + return result + + +class Cli(NetCli): + + CLI_PROMPTS_RE = [ + re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"), + re.compile(r"\@[\w\-\.]+:\S+?[>#\$] ?$") + ] + + CLI_ERRORS_RE = [ + re.compile(r"\n\s*Invalid command:"), + re.compile(r"\nCommit failed"), + re.compile(r"\n\s+Set failed"), + ] + + def connect(self, params, **kwargs): + super(Cli, self).connect(params, kickstart=False, **kwargs) + self.shell.send('set terminal length 0') + self._connected = True + + ### Cli methods ### + + def run_commands(self, commands, **kwargs): + commands = to_list(commands) + response = self.execute([str(c) for c in commands]) + return response + + ### Config methods ### + + def configure(self, commands, commit=True, **kwargs): + """Called by Config.__call__ + """ + cmds = ['configure'] + cmds.extend(to_list(commands)) + response = self.execute(cmds) + if commit: + self.commit_config() + return response + + def load_config(self, commands): + self.configure(commands, commit=False) + diff = None + if not self.execute('compare')[0].startswith('No changes'): + diff = self.execute(['show'])[0] + return diff + + def get_config(self): + return self.execute(['show configuration commands'])[0] + + def commit_config(self, confirm=0, comment=None): + if confirm > 0: + cmd = 'commit-confirm %s' % confirm + else: + cmd = 'commit' + if comment: + cmd += ' comment "%s"' % comment + self.execute([cmd]) + + def abort_config(self): + self.execute(['discard']) + + def save_config(self): + self.execute(['save']) +Cli = register_transport('cli', default=True)(Cli)