From 2571d2f64b3aebd42ba1413e0cb4c65836c1e1e7 Mon Sep 17 00:00:00 2001 From: Ricardo Carrillo Cruz Date: Wed, 5 Apr 2017 13:27:52 +0200 Subject: [PATCH] Refactor openvswitch_db module (#23288) The openvswitch_db module uses the ovs-vsctl binary to address changes. On other network modules we follow the pattern of returning 'commands' as part of the result, containing the commands run on the target device. Follow that for code consistency and maintenance. Also, adding state param, which allows to add/remove keys on columns. --- .../modules/network/ovs/openvswitch_db.py | 139 +++++++++++++----- 1 file changed, 103 insertions(+), 36 deletions(-) diff --git a/lib/ansible/modules/network/ovs/openvswitch_db.py b/lib/ansible/modules/network/ovs/openvswitch_db.py index dd71d3b310a..f20e1e33243 100644 --- a/lib/ansible/modules/network/ovs/openvswitch_db.py +++ b/lib/ansible/modules/network/ovs/openvswitch_db.py @@ -38,6 +38,16 @@ requirements: [ "ovs-vsctl >= 2.3.3" ] description: - Set column values in record in database table. options: + state: + required: false + description: + - Configures the state of the key. When set + to I(present), the I(key) and I(value) pair will be set + on the I(record) and when set to I(absent) the I(key) + will not be set. + default: present + choices: ['present', 'absent'] + version_added: "2.4" table: required: true description: @@ -82,58 +92,114 @@ EXAMPLES = ''' col: other_config key: disable-in-band value: true + +# Remove in band key +- openvswitch_db: + state: present + table: Bridge + record: br-int + col: other_config + key: disable-in-band ''' -def cmd_run(module, cmd, check_rc=True): - """ Log and run ovs-vsctl command. """ - return module.run_command(cmd.split(" "), check_rc=check_rc) +def map_obj_to_command(want, have, module): + """ Define ovs-vsctl command to meet desired state """ + command = None + if module.params['state'] == 'absent': + if 'key' in have.keys(): + templatized_command = "%(ovs-vsctl)s -t %(timeout)s remove %(table)s %(record)s " \ + "%(col)s %(key)s=%(value)s" + command = templatized_command % module.params + else: + if 'key' not in have.keys(): + templatized_command = "%(ovs-vsctl)s -t %(timeout)s add %(table)s %(record)s " \ + "%(col)s %(key)s=%(value)s" + command = templatized_command % module.params + elif want['value'] != have['value']: + templatized_command = "%(ovs-vsctl)s -t %(timeout)s set %(table)s %(record)s " \ + "%(col)s:%(key)s=%(value)s" + command = templatized_command % module.params -def params_set(module): - """ Implement the ovs-vsctl set commands. """ + return command - changed = False - ## - # Place in params dictionary in order to support the string format below. - module.params["ovs-vsctl"] = module.get_bin_path("ovs-vsctl", True) +def map_config_to_obj(module): + templatized_command = "%(ovs-vsctl)s -t %(timeout)s list %(table)s %(record)s" + command = templatized_command % module.params + rc, out, err = module.run_command(command, check_rc=True) + + if rc != 0: + module.fail_json(msg=err) - fmt = "%(ovs-vsctl)s -t %(timeout)s get %(table)s %(record)s " \ - "%(col)s:%(key)s" + match = re.search(r'^' + module.params['col'] + r'(\s+):(\s+)(.*)$', out, re.M) - cmd = fmt % module.params + col_value = match.group(3) + col_value_to_dict = {} + if match.group(3): + for kv in col_value[1:-1].split(','): + k, v = kv.split('=') + col_value_to_dict[k.strip()] = v.strip() - (_, output, _) = cmd_run(module, cmd, False) - if module.params['value'] not in output: - fmt = "%(ovs-vsctl)s -t %(timeout)s set %(table)s %(record)s " \ - "%(col)s:%(key)s=%(value)s" - cmd = fmt % module.params - ## - # Check if flow exists and is the same. - (rtc, _, err) = cmd_run(module, cmd) - if rtc != 0: - module.fail_json(msg=err) - changed = True - module.exit_json(changed=changed) + obj = { + 'table': module.params['table'], + 'record': module.params['record'], + 'col': module.params['col'], + } + + if module.params['key'] in col_value_to_dict: + obj['key'] = module.params['key'] + obj['value'] = col_value_to_dict[module.params['key']] + + return obj + + +def map_params_to_obj(module): + obj = { + 'table': module.params['table'], + 'record': module.params['record'], + 'col': module.params['col'], + 'key': module.params['key'], + 'value': module.params['value'] + } + + return obj # pylint: disable=E0602 def main(): """ Entry point for ansible module. """ - module = AnsibleModule( - argument_spec={ - 'table': {'required': True}, - 'record': {'required': True}, - 'col': {'required': True}, - 'key': {'required': True}, - 'value': {'required': True}, - 'timeout': {'default': 5, 'type': 'int'}, - }, - supports_check_mode=True, - ) + argument_spec = { + 'state': {'default': 'present', 'choices': ['present', 'absent']}, + 'table': {'required': True}, + 'record': {'required': True}, + 'col': {'required': True}, + 'key': {'required': True}, + 'value': {'required': True}, + 'timeout': {'default': 5, 'type': 'int'}, + } + + module = AnsibleModule(argument_spec=argument_spec, + supports_check_mode=True) + + result = {'changed': False} + + # We add ovs-vsctl to module_params to later build up templatized commands + module.params["ovs-vsctl"] = module.get_bin_path("ovs-vsctl", True) + + want = map_params_to_obj(module) + have = map_config_to_obj(module) + + command = map_obj_to_command(want, have, module) + result['command'] = command + + if command: + if not module.check_mode: + module.run_command(command, check_rc=True) + result['changed'] = True - params_set(module) + module.exit_json(**result) # pylint: disable=W0614 @@ -142,6 +208,7 @@ def main(): # import module snippets from ansible.module_utils.basic import * +import re if __name__ == '__main__': main()