From 325ef12aeedc71d9e9da66aebbb66caaab64f9bf Mon Sep 17 00:00:00 2001 From: Mark Hamilton Date: Thu, 9 Jul 2015 00:36:30 -0700 Subject: [PATCH 1/3] Added support to assign attached mac address interface id and port options. Updated code to pass pep8 v1.6.2 pylint v0.25.0. --- network/openvswitch_port.py | 212 +++++++++++++++++++++++++++++++++--- 1 file changed, 196 insertions(+), 16 deletions(-) diff --git a/network/openvswitch_port.py b/network/openvswitch_port.py index 6f59f4b134b..bb3924161b7 100644 --- a/network/openvswitch_port.py +++ b/network/openvswitch_port.py @@ -1,6 +1,8 @@ #!/usr/bin/python #coding: utf-8 -*- +# pylint: disable=C0111 + # (c) 2013, David Stygstra # # This file is part of Ansible @@ -17,6 +19,11 @@ # # You should have received a copy of the GNU General Public License # along with this software. If not, see . +# +# Portions copyright @ 2015 VMware, Inc. All rights reserved. + +import syslog +import os DOCUMENTATION = ''' --- @@ -47,44 +54,188 @@ options: default: 5 description: - How long to wait for ovs-vswitchd to respond + iface-id: + required: false + description: + - Used when port is created to set the external_ids:iface-id. + attached-mac: + required: false + description: + - MAC address when port is created using external_ids:attached-mac. + set: + required: false + description: + - Additional set options to apply to the port ''' EXAMPLES = ''' # Creates port eth2 on bridge br-ex - openvswitch_port: bridge=br-ex port=eth2 state=present + +# Creates port eth6 and set ofport equal to 6. +- openvswitch_port: bridge=bridge-loop port=eth6 state=present + set Interface eth6 ofport_request=6 + +# Assign interface id server1-vifeth6 and mac address 52:54:00:30:6d:11 +# to port vifeth6 +- openvswitch_port: bridge=br-int port=vifeth6 state=present + args: + external_ids: + iface-id: "server1-vifeth6" + attached-mac: "52:54:00:30:6d:11" + ''' +# 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): + """ 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("=") + syslog.syslog(syslog.LOG_NOTICE, "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.state = module.params['state'] self.timeout = module.params['timeout'] + self.set_opt = module.params.get('set', None) + + # if the port name starts with "vif", let's assume it's a VIF + self.is_vif = self.port.startswith('vif') + + ## + # Several places need host name. + rtc, out, err = self.module.run_command(["hostname", "-s"]) + if (rtc): + self.module.fail_json(msg=err) + self.hostname = out.strip("\n") + + def _attached_mac_get(self): + """ Return the interface. """ + attached_mac = self.module.params['external_ids'].get('attached-mac', + None) + if (not attached_mac): + attached_mac = "00:50:50:F" + attached_mac += self.hostname[-3] + ":" + attached_mac += self.hostname[-2:] + ":00" + return attached_mac + + def _iface_id_get(self): + """ Return the interface id. """ + + iface_id = self.module.params['external_ids'].get('iface-id', None) + if (not iface_id): + iface_id = "%s-%s" % (self.hostname, self.port) + return iface_id def _vsctl(self, command): '''Run ovs-vsctl command''' - return self.module.run_command(['ovs-vsctl', '-t', str(self.timeout)] + command) + + cmd = ['ovs-vsctl', '-t', str(self.timeout)] + command + syslog.syslog(syslog.LOG_NOTICE, " ".join(cmd)) + return self.module.run_command(cmd) def exists(self): '''Check if the port already exists''' - rc, out, err = self._vsctl(['list-ports', self.bridge]) - if rc != 0: - raise Exception(err) + + (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')) + def set(self, set_opt): + """ Set attributes on a port. """ + + syslog.syslog(syslog.LOG_NOTICE, "set called %s" % set_opt) + if (not set_opt): + return False + + (get_cmd, set_value) = _set_to_get(set_opt) + (rtc, out, err) = self._vsctl(get_cmd) + if rtc != 0: + self.module.fail_json(msg=err) + + 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) + self.module.exit_json(changed=True) + syslog.syslog(syslog.LOG_NOTICE, "-- set %s" % set_opt) + + def set_vif_attributes(self): + ''' Set attributes for a vif ''' + + ## + # create a fake MAC address for the VIF + fmac = self._attached_mac_get() + + ## + # If vif_uuid is missing then construct a new one. + iface_id = self._iface_id_get() + syslog.syslog(syslog.LOG_NOTICE, "iface-id %s" % iface_id) + + attached_mac = "external_ids:attached-mac=%s" % fmac + iface_id = "external_ids:iface-id=%s" % iface_id + vm_id = "external_ids:vm-id=%s" % self.hostname + cmd = ["set", "Interface", self.port, + "external_ids:iface-status=active", iface_id, vm_id, + attached_mac] + + (rtc, _, stderr) = self._vsctl(cmd) + if rtc != 0: + self.module.fail_json(msg="%s returned %s %s" % (" ".join(cmd), + rtc, stderr)) + def add(self): '''Add the port''' - rc, _, err = self._vsctl(['add-port', self.bridge, self.port]) - if rc != 0: - raise Exception(err) + cmd = ['add-port', self.bridge, self.port] + 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) + syslog.syslog(syslog.LOG_NOTICE, " ".join(cmd)) + + if self.is_vif: + self.set_vif_attributes() def delete(self): '''Remove the port''' - rc, _, err = self._vsctl(['del-port', self.bridge, self.port]) - if rc != 0: - raise Exception(err) + (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''' @@ -95,8 +246,8 @@ class OVSPort(object): changed = True else: changed = False - except Exception, e: - self.module.fail_json(msg=str(e)) + except Exception, earg: + self.module.fail_json(msg=str(earg)) self.module.exit_json(changed=changed) def run(self): @@ -108,25 +259,50 @@ class OVSPort(object): self.delete() changed = True elif self.state == 'present': - if not self.exists(): + ## + # Add any missing ports. + if (not self.exists()): self.add() changed = True - except Exception, e: - self.module.fail_json(msg=str(e)) + + ## + # 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) + syslog.syslog(syslog.LOG_NOTICE, + "external %s" % external_id) + changed = self.set(external_id) or changed + ## + except Exception, earg: + 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}, 'state': {'default': 'present', 'choices': ['present', 'absent']}, - 'timeout': {'default': 5, 'type': 'int'} + 'timeout': {'default': 5, 'type': 'int'}, + 'set': {'required': False}, + 'external_ids': {'default': {}, 'required': False}, + 'syslogging': {'required': False, 'type': "bool", 'default': True} }, supports_check_mode=True, ) + if (module.params["syslogging"]): + syslog.openlog('ansible-%s' % os.path.basename(__file__)) + port = OVSPort(module) if module.check_mode: port.check() @@ -134,6 +310,10 @@ def main(): port.run() +# pylint: disable=W0614 +# pylint: disable=W0401 +# pylint: disable=W0622 + # import module snippets from ansible.module_utils.basic import * main() From 0395de2f098faa1f532c946a014294d7ae9109cc Mon Sep 17 00:00:00 2001 From: Mark Hamilton Date: Tue, 14 Jul 2015 01:49:16 -0700 Subject: [PATCH 2/3] Added version_added and default to new parameters external_ids and set. Generalized external_ids to handle any parameter. --- network/openvswitch_port.py | 104 +++++++++--------------------------- 1 file changed, 26 insertions(+), 78 deletions(-) diff --git a/network/openvswitch_port.py b/network/openvswitch_port.py index bb3924161b7..3f9c22fe432 100644 --- a/network/openvswitch_port.py +++ b/network/openvswitch_port.py @@ -5,6 +5,8 @@ # (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 @@ -19,8 +21,6 @@ # # You should have received a copy of the GNU General Public License # along with this software. If not, see . -# -# Portions copyright @ 2015 VMware, Inc. All rights reserved. import syslog import os @@ -54,18 +54,18 @@ options: default: 5 description: - How long to wait for ovs-vswitchd to respond - iface-id: + external_ids: + version_added: 2.0 required: false + default: {} description: - - Used when port is created to set the external_ids:iface-id. - attached-mac: - required: false - description: - - MAC address when port is created using external_ids:attached-mac. + - Dictionary of external_ids applied to a port. set: + version_added: 2.0 required: false + default: None description: - - Additional set options to apply to the port + - Set a single property on a port. ''' EXAMPLES = ''' @@ -77,13 +77,14 @@ EXAMPLES = ''' set Interface eth6 ofport_request=6 # Assign interface id server1-vifeth6 and mac address 52:54:00:30:6d:11 -# to port vifeth6 +# 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: "server1-vifeth6" + iface-id: "{{inventory_hostname}}-vifeth6" attached-mac: "52:54:00:30:6d:11" - + vm-id: "{{inventory_hostname}}" + iface-status: "active" ''' # pylint: disable=W0703 @@ -124,40 +125,12 @@ class OVSPort(object): self.timeout = module.params['timeout'] self.set_opt = module.params.get('set', None) - # if the port name starts with "vif", let's assume it's a VIF - self.is_vif = self.port.startswith('vif') - - ## - # Several places need host name. - rtc, out, err = self.module.run_command(["hostname", "-s"]) - if (rtc): - self.module.fail_json(msg=err) - self.hostname = out.strip("\n") - - def _attached_mac_get(self): - """ Return the interface. """ - attached_mac = self.module.params['external_ids'].get('attached-mac', - None) - if (not attached_mac): - attached_mac = "00:50:50:F" - attached_mac += self.hostname[-3] + ":" - attached_mac += self.hostname[-2:] + ":00" - return attached_mac - - def _iface_id_get(self): - """ Return the interface id. """ - - iface_id = self.module.params['external_ids'].get('iface-id', None) - if (not iface_id): - iface_id = "%s-%s" % (self.hostname, self.port) - return iface_id - - def _vsctl(self, command): + def _vsctl(self, command, check_rc=True): '''Run ovs-vsctl command''' cmd = ['ovs-vsctl', '-t', str(self.timeout)] + command syslog.syslog(syslog.LOG_NOTICE, " ".join(cmd)) - return self.module.run_command(cmd) + return self.module.run_command(cmd, check_rc=check_rc) def exists(self): '''Check if the port already exists''' @@ -171,50 +144,29 @@ class OVSPort(object): def set(self, set_opt): """ Set attributes on a port. """ - syslog.syslog(syslog.LOG_NOTICE, "set called %s" % set_opt) if (not set_opt): return False (get_cmd, set_value) = _set_to_get(set_opt) - (rtc, out, err) = self._vsctl(get_cmd) + (rtc, out, err) = self._vsctl(get_cmd, False) if rtc != 0: - self.module.fail_json(msg=err) + ## + # 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('"') - 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) - self.module.exit_json(changed=True) - syslog.syslog(syslog.LOG_NOTICE, "-- set %s" % set_opt) - def set_vif_attributes(self): - ''' Set attributes for a vif ''' - - ## - # create a fake MAC address for the VIF - fmac = self._attached_mac_get() - - ## - # If vif_uuid is missing then construct a new one. - iface_id = self._iface_id_get() - syslog.syslog(syslog.LOG_NOTICE, "iface-id %s" % iface_id) - - attached_mac = "external_ids:attached-mac=%s" % fmac - iface_id = "external_ids:iface-id=%s" % iface_id - vm_id = "external_ids:vm-id=%s" % self.hostname - cmd = ["set", "Interface", self.port, - "external_ids:iface-status=active", iface_id, vm_id, - attached_mac] - - (rtc, _, stderr) = self._vsctl(cmd) - if rtc != 0: - self.module.fail_json(msg="%s returned %s %s" % (" ".join(cmd), - rtc, stderr)) + return True def add(self): '''Add the port''' @@ -226,10 +178,8 @@ class OVSPort(object): (rtc, _, err) = self._vsctl(cmd) if rtc != 0: self.module.fail_json(msg=err) - syslog.syslog(syslog.LOG_NOTICE, " ".join(cmd)) - if self.is_vif: - self.set_vif_attributes() + return True def delete(self): '''Remove the port''' @@ -275,8 +225,6 @@ class OVSPort(object): value = value.replace('"', '') fmt_opt = "Interface %s external_ids:%s=%s" external_id = fmt_opt % (self.port, key, value) - syslog.syslog(syslog.LOG_NOTICE, - "external %s" % external_id) changed = self.set(external_id) or changed ## except Exception, earg: @@ -293,7 +241,7 @@ def main(): 'port': {'required': True}, 'state': {'default': 'present', 'choices': ['present', 'absent']}, 'timeout': {'default': 5, 'type': 'int'}, - 'set': {'required': False}, + 'set': {'required': False, 'default': None}, 'external_ids': {'default': {}, 'required': False}, 'syslogging': {'required': False, 'type': "bool", 'default': True} }, From 018db7d068a31e8f477e1a90a3454c3b7c0b7d56 Mon Sep 17 00:00:00 2001 From: Mark Hamilton Date: Tue, 14 Jul 2015 02:10:57 -0700 Subject: [PATCH 3/3] removed syslog.openlog --- network/openvswitch_port.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/network/openvswitch_port.py b/network/openvswitch_port.py index 3f9c22fe432..469d53730da 100644 --- a/network/openvswitch_port.py +++ b/network/openvswitch_port.py @@ -23,7 +23,6 @@ # along with this software. If not, see . import syslog -import os DOCUMENTATION = ''' --- @@ -243,14 +242,10 @@ def main(): 'timeout': {'default': 5, 'type': 'int'}, 'set': {'required': False, 'default': None}, 'external_ids': {'default': {}, 'required': False}, - 'syslogging': {'required': False, 'type': "bool", 'default': True} }, supports_check_mode=True, ) - if (module.params["syslogging"]): - syslog.openlog('ansible-%s' % os.path.basename(__file__)) - port = OVSPort(module) if module.check_mode: port.check()