#!/usr/bin/python #coding: utf-8 -*- # pylint: disable=C0111 # (c) 2013, David Stygstra # # Portions copyright @ 2015 VMware, Inc. # # This file is part of Ansible # # This module 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. # # This software 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 this software. If not, see . DOCUMENTATION = ''' --- module: openvswitch_port version_added: 1.4 author: "David Stygstra (@stygstra)" short_description: Manage Open vSwitch ports requirements: [ ovs-vsctl ] description: - Manage Open vSwitch ports options: bridge: required: true description: - Name of bridge to manage port: required: true description: - Name of port to manage on the bridge tag: version_added: 2.2 required: false description: - VLAN tag for this port state: required: false default: "present" choices: [ present, absent ] description: - Whether the port should exist timeout: required: false default: 5 description: - How long to wait for ovs-vswitchd to respond external_ids: version_added: 2.0 required: false default: {} description: - Dictionary of external_ids applied to a port. set: version_added: 2.0 required: false default: None description: - Set a single property on a port. ''' EXAMPLES = ''' # Creates port eth2 on bridge br-ex - openvswitch_port: bridge: br-ex port: eth2 state: present # Creates port eth6 - openvswitch_port: bridge: bridge-loop port: eth6 state: present set: Interface eth6 # Creates port vlan10 with tag 10 on bridge br-ex - openvswitch_port: bridge: br-ex port: vlan10 tag: 10 state: present set: Interface vlan10 # Assign interface id server1-vifeth6 and mac address 00:00:5E:00:53:23 # to port vifeth6 and setup port to be managed by a controller. - openvswitch_port: bridge: br-int port: vifeth6 state: present args: external_ids: iface-id: '{{ inventory_hostname }}-vifeth6' attached-mac: '00:00:5E:00:53:23' vm-id: '{{ inventory_hostname }}' iface-status: active ''' # pylint: disable=W0703 def truncate_before(value, srch): """ Return content of str before the srch parameters. """ before_index = value.find(srch) if (before_index >= 0): return value[:before_index] else: return value def _set_to_get(set_cmd, module): """ Convert set command to get command and set value. return tuple (get command, set value) """ ## # If set has option: then we want to truncate just before that. set_cmd = truncate_before(set_cmd, " option:") get_cmd = set_cmd.split(" ") (key, value) = get_cmd[-1].split("=") module.log("get commands %s " % key) return (["--", "get"] + get_cmd[:-1] + [key], value) # pylint: disable=R0902 class OVSPort(object): """ Interface to OVS port. """ def __init__(self, module): self.module = module self.bridge = module.params['bridge'] self.port = module.params['port'] self.tag = module.params['tag'] self.state = module.params['state'] self.timeout = module.params['timeout'] self.set_opt = module.params.get('set', None) def _vsctl(self, command, check_rc=True): '''Run ovs-vsctl command''' cmd = ['ovs-vsctl', '-t', str(self.timeout)] + command return self.module.run_command(cmd, check_rc=check_rc) def exists(self): '''Check if the port already exists''' (rtc, out, err) = self._vsctl(['list-ports', self.bridge]) if rtc != 0: self.module.fail_json(msg=err) return any(port.rstrip() == self.port for port in out.split('\n')) or self.port == self.bridge def set(self, set_opt): """ Set attributes on a port. """ self.module.log("set called %s" % set_opt) if (not set_opt): return False (get_cmd, set_value) = _set_to_get(set_opt, self.module) (rtc, out, err) = self._vsctl(get_cmd, False) if rtc != 0: ## # ovs-vsctl -t 5 -- get Interface port external_ids:key # returns failure if key does not exist. out = None else: out = out.strip("\n") out = out.strip('"') if (out == set_value): return False (rtc, out, err) = self._vsctl(["--", "set"] + set_opt.split(" ")) if rtc != 0: self.module.fail_json(msg=err) return True def add(self): '''Add the port''' cmd = ['add-port', self.bridge, self.port] if self.tag: cmd += ["tag=" + self.tag] if self.set and self.set_opt: cmd += ["--", "set"] cmd += self.set_opt.split(" ") (rtc, _, err) = self._vsctl(cmd) if rtc != 0: self.module.fail_json(msg=err) return True def delete(self): '''Remove the port''' (rtc, _, err) = self._vsctl(['del-port', self.bridge, self.port]) if rtc != 0: self.module.fail_json(msg=err) def check(self): '''Run check mode''' try: if self.state == 'absent' and self.exists(): changed = True elif self.state == 'present' and not self.exists(): changed = True else: changed = False except Exception: earg = get_exception() self.module.fail_json(msg=str(earg)) self.module.exit_json(changed=changed) def run(self): '''Make the necessary changes''' changed = False try: if self.state == 'absent': if self.exists(): self.delete() changed = True elif self.state == 'present': ## # Add any missing ports. if (not self.exists()): self.add() changed = True ## # If the -- set changed check here and make changes # but this only makes sense when state=present. if (not changed): changed = self.set(self.set_opt) or changed items = self.module.params['external_ids'].items() for (key, value) in items: value = value.replace('"', '') fmt_opt = "Interface %s external_ids:%s=%s" external_id = fmt_opt % (self.port, key, value) changed = self.set(external_id) or changed ## except Exception: earg = get_exception() self.module.fail_json(msg=str(earg)) self.module.exit_json(changed=changed) # pylint: disable=E0602 def main(): """ Entry point. """ module = AnsibleModule( argument_spec={ 'bridge': {'required': True}, 'port': {'required': True}, 'tag': {'required': False}, 'state': {'default': 'present', 'choices': ['present', 'absent']}, 'timeout': {'default': 5, 'type': 'int'}, 'set': {'required': False, 'default': None}, 'external_ids': {'default': {}, 'required': False, 'type': 'dict'}, }, supports_check_mode=True, ) port = OVSPort(module) if module.check_mode: port.check() else: port.run() # pylint: disable=W0614 # pylint: disable=W0401 # pylint: disable=W0622 # import module snippets from ansible.module_utils.basic import * from ansible.module_utils.pycompat24 import get_exception if __name__ == '__main__': main()