From 0faeedf5c381659d62164ee503127bca0b6897fd Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 2 Aug 2019 11:33:35 +0200 Subject: [bridge] T1156: first working implementation using Python and XML --- src/conf_mode/interface-bridge.py | 227 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100755 src/conf_mode/interface-bridge.py (limited to 'src/conf_mode/interface-bridge.py') diff --git a/src/conf_mode/interface-bridge.py b/src/conf_mode/interface-bridge.py new file mode 100755 index 000000000..f7f70b15d --- /dev/null +++ b/src/conf_mode/interface-bridge.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program 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 program. If not, see . +# +# + +import os +import sys +import copy +import subprocess + +import vyos.configinterface as VyIfconfig + +from vyos.config import Config +from vyos import ConfigError + +default_config_data = { + 'address': [], + 'aging': '300', + 'br_name': '', + 'description': '', + 'deleted': False, + 'dhcp_client_id': '', + 'dhcp_hostname': '', + 'dhcpv6_parameters_only': False, + 'dhcpv6_temporary': False, + 'disable': False, + 'disable_link_detect': False, + 'forwarding_delay': '15', + 'hello_time': '2', + 'igmp_querier': 0, + 'arp_cache_timeout_ms': '30000', + 'mac' : '', + 'max_age': '20', + 'priority': '32768', + 'stp': 'off' +} + +def subprocess_cmd(command): + process = subprocess.Popen(command,stdout=subprocess.PIPE, shell=True) + proc_stdout = process.communicate()[0].strip() + print(proc_stdout) + +def get_config(): + bridge = copy.deepcopy(default_config_data) + conf = Config() + + # determine tagNode instance + try: + bridge['br_name'] = os.environ['VYOS_TAGNODE_VALUE'] + print("Executing script for interface: " + bridge['br_name']) + except KeyError as E: + print("Interface not specified") + + # Check if bridge has been removed + if not conf.exists('interfaces bridge ' + bridge['br_name']): + bridge['deleted'] = True + return bridge + + # set new configuration level + conf.set_level('interfaces bridge ' + bridge['br_name']) + + # retrieve configured interface addresses + if conf.exists('address'): + bridge['address'] = conf.return_values('address') + + # retrieve aging - how long addresses are retained + if conf.exists('aging'): + bridge['aging'] = conf.return_value('aging') + + # retrieve interface description + if conf.exists('description'): + bridge['description'] = conf.return_value('description') + + # DHCP client identifier + if conf.exists('dhcp-options client-id'): + bridge['dhcp_client_id'] = conf.return_value('dhcp-options client-id') + + # DHCP client hostname + if conf.exists('dhcp-options host-name'): + bridge['dhcp_hostname'] = conf.return_value('dhcp-options host-name') + + # DHCPv6 acquire only config parameters, no address + if conf.exists('dhcpv6-options parameters-only'): + bridge['dhcpv6_parameters_only'] = True + + # DHCPv6 IPv6 "temporary" address + if conf.exists('dhcpv6-options temporary'): + bridge['dhcpv6_temporary'] = True + + # Disable this bridge interface + if conf.exists('disable'): + bridge['disable'] = True + + # Ignore link state changes + if conf.exists('disable-link-detect'): + bridge['disable_link_detect'] = True + + # Forwarding delay + if conf.exists('forwarding-delay'): + bridge['forwarding_delay'] = conf.return_value('forwarding-delay') + + # Hello packet advertisment interval + if conf.exists('hello-time'): + bridge['hello_time'] = conf.return_value('hello-time') + + # Enable or disable IGMP querier + if conf.exists('igmp-snooping querier'): + tmp = conf.return_value('igmp-snooping querier') + if tmp == "enable": + bridge['igmp_querier'] = 1 + + # ARP cache entry timeout in seconds + if conf.exists('ip arp-cache-timeout'): + tmp = 1000 * int(conf.return_value('ip arp-cache-timeout')) + bridge['arp_cache_timeout_ms'] = str(tmp) + + # Media Access Control (MAC) address + if conf.exists('mac'): + bridge['mac'] = conf.return_value('mac') + + # Interval at which neighbor bridges are removed + if conf.exists('max-age'): + bridge['max_age'] = conf.return_value('max-age') + + # Priority for this bridge + if conf.exists('priority'): + bridge['priority'] = conf.return_value('priority') + + # Enable spanning tree protocol + if conf.exists('stp'): + tmp = conf.return_value('stp') + if tmp == "true": + bridge['stp'] = 'on' + + return bridge + +def verify(bridge): + if bridge is None: + return None + + return None + +def generate(bridge): + if bridge is None: + return None + + return None + +def apply(bridge): + if bridge is None: + return None + + if bridge['deleted']: + # bridges need to be shutdown first + os.system("ip link set dev {0} down".format(bridge['br_name'])) + # delete bridge + os.system("brctl delbr {0}".format(bridge['br_name'])) + else: + # create bridge if it does not exist + if not os.path.exists("/sys/class/net/" + bridge['br_name']): + os.system("brctl addbr {0}".format(bridge['br_name'])) + + # assemble bridge configuration + # configuration is passed via subprocess to brctl + cmd = '' + + # set ageing time + cmd += 'brctl setageing {0} {1}'.format(bridge['br_name'], bridge['aging']) + cmd += ' && ' + + # set bridge forward delay + cmd += 'brctl setfd {0} {1}'.format(bridge['br_name'], bridge['forwarding_delay']) + cmd += ' && ' + + # set hello time + cmd += 'brctl sethello {0} {1}'.format(bridge['br_name'], bridge['hello_time']) + cmd += ' && ' + + # set max message age + cmd += 'brctl setmaxage {0} {1}'.format(bridge['br_name'], bridge['max_age']) + cmd += ' && ' + + # set bridge priority + cmd += 'brctl setbridgeprio {0} {1}'.format(bridge['br_name'], bridge['priority']) + cmd += ' && ' + + # turn stp on/off + cmd += 'brctl stp {0} {1}'.format(bridge['br_name'], bridge['stp']) + + subprocess_cmd(cmd) + + # update interface description used e.g. within SNMP + VyIfconfig.set_description(bridge['br_name'], bridge['description']) + + # Ignore link state changes? + VyIfconfig.set_link_detect(bridge['br_name'], bridge['disable_link_detect']) + + # enable or disable IGMP querier + VyIfconfig.set_multicast_querier(bridge['br_name'], bridge['igmp_querier']) + + # ARP cache entry timeout in seconds + VyIfconfig.set_arp_cache_timeout(bridge['br_name'], bridge['arp_cache_timeout_ms']) + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + sys.exit(1) -- cgit v1.2.3 From 5b836709a15e4f6a8775e5dc26609febd5bc2480 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 2 Aug 2019 17:27:25 +0200 Subject: [bridge] T1156: support adding and removing bridge member interfaces This is the new syntax bridge br0 { member { interface eth0 { cost 10 } interface eth1 { cost 11 } } } --- interface-definitions/interfaces-bridge.xml | 76 +++++++++++++++---------- python/vyos/configinterface.py | 8 +++ src/conf_mode/interface-bridge.py | 86 +++++++++++++++++++++++------ src/migration-scripts/interface/0-to-1 | 82 --------------------------- src/migration-scripts/interfaces/0-to-1 | 82 +++++++++++++++++++++++++++ 5 files changed, 207 insertions(+), 127 deletions(-) delete mode 100755 src/migration-scripts/interface/0-to-1 create mode 100755 src/migration-scripts/interfaces/0-to-1 (limited to 'src/conf_mode/interface-bridge.py') diff --git a/interface-definitions/interfaces-bridge.xml b/interface-definitions/interfaces-bridge.xml index a5f2df5b5..af19d9438 100644 --- a/interface-definitions/interfaces-bridge.xml +++ b/interface-definitions/interfaces-bridge.xml @@ -51,7 +51,8 @@ Address aging time for bridge seconds (default 300) - + + @@ -146,20 +147,7 @@ Enable or disable IGMP querier - - enable disable - - - enable - Enable IGMP querier - - - disable - Disable IGMP querier - - - (enable|disable) - + @@ -206,6 +194,49 @@ Bridge max aging value must be between 1 and 40 seconds + + + Bridge member interfaces + + + + + Member interface name + + + + + + + + Bridge port cost + + 1-65535 + Path cost value for Spanning Tree Protocol + + + + + Path cost value must be between 1 and 65535 + + + + + Bridge port priority + + 0-63 + Bridge port priority + + + + + Port priority value must be between 0 and 63 + + + + + + Priority for this bridge @@ -222,20 +253,7 @@ Enable spanning tree protocol - - true false - - - true - Enable Spanning Tree Protocol - - - false - Disable Spanning Tree Protocol - - - (true|false) - + diff --git a/python/vyos/configinterface.py b/python/vyos/configinterface.py index b0d766b9c..37b6b92c1 100644 --- a/python/vyos/configinterface.py +++ b/python/vyos/configinterface.py @@ -15,6 +15,14 @@ import os +def set_mac_address(intf, addr): + """ + Configure interface mac address using iproute2 command + + NOTE: mac address should be validated here??? + """ + os.system('ip link set {} address {}'.format(intf, addr)) + def set_description(intf, desc): """ Sets the interface secription reported usually by SNMP diff --git a/src/conf_mode/interface-bridge.py b/src/conf_mode/interface-bridge.py index f7f70b15d..637c58a5e 100755 --- a/src/conf_mode/interface-bridge.py +++ b/src/conf_mode/interface-bridge.py @@ -44,6 +44,8 @@ default_config_data = { 'arp_cache_timeout_ms': '30000', 'mac' : '', 'max_age': '20', + 'member': [], + 'member_remove': [], 'priority': '32768', 'stp': 'off' } @@ -53,6 +55,10 @@ def subprocess_cmd(command): proc_stdout = process.communicate()[0].strip() print(proc_stdout) +def diff(first, second): + second = set(second) + return [item for item in first if item not in second] + def get_config(): bridge = copy.deepcopy(default_config_data) conf = Config() @@ -60,7 +66,6 @@ def get_config(): # determine tagNode instance try: bridge['br_name'] = os.environ['VYOS_TAGNODE_VALUE'] - print("Executing script for interface: " + bridge['br_name']) except KeyError as E: print("Interface not specified") @@ -118,9 +123,7 @@ def get_config(): # Enable or disable IGMP querier if conf.exists('igmp-snooping querier'): - tmp = conf.return_value('igmp-snooping querier') - if tmp == "enable": - bridge['igmp_querier'] = 1 + bridge['igmp_querier'] = 1 # ARP cache entry timeout in seconds if conf.exists('ip arp-cache-timeout'): @@ -135,15 +138,35 @@ def get_config(): if conf.exists('max-age'): bridge['max_age'] = conf.return_value('max-age') + # Determine bridge member interface (currently configured) + for intf in conf.list_nodes('member interface'): + iface = { + 'name': intf, + 'cost': '', + 'priority': '' + } + + if conf.exists('member interface {} cost'.format(intf)): + iface['cost'] = conf.return_value('member interface {} cost'.format(intf)) + + if conf.exists('member interface {} priority'.format(intf)): + iface['priority'] = conf.return_value('member interface {} priority'.format(intf)) + + bridge['member'].append(iface) + + # Determine bridge member interface (currently effective) - to determine which interfaces + # need to be removed from the bridge + eff_intf = conf.list_effective_nodes('member interface') + act_intf = conf.list_nodes('member interface') + bridge['member_remove'] = diff(eff_intf, act_intf) + # Priority for this bridge if conf.exists('priority'): bridge['priority'] = conf.return_value('priority') # Enable spanning tree protocol if conf.exists('stp'): - tmp = conf.return_value('stp') - if tmp == "true": - bridge['stp'] = 'on' + bridge['stp'] = 'on' return bridge @@ -151,6 +174,9 @@ def verify(bridge): if bridge is None: return None + # validate agains other bridge interfaces that the interface is not assigned + # to another bridge + return None def generate(bridge): @@ -165,43 +191,71 @@ def apply(bridge): if bridge['deleted']: # bridges need to be shutdown first - os.system("ip link set dev {0} down".format(bridge['br_name'])) + os.system("ip link set dev {} down".format(bridge['br_name'])) # delete bridge - os.system("brctl delbr {0}".format(bridge['br_name'])) + os.system("brctl delbr {}".format(bridge['br_name'])) else: # create bridge if it does not exist if not os.path.exists("/sys/class/net/" + bridge['br_name']): - os.system("brctl addbr {0}".format(bridge['br_name'])) + os.system("brctl addbr {}".format(bridge['br_name'])) # assemble bridge configuration # configuration is passed via subprocess to brctl cmd = '' # set ageing time - cmd += 'brctl setageing {0} {1}'.format(bridge['br_name'], bridge['aging']) + cmd += 'brctl setageing {} {}'.format(bridge['br_name'], bridge['aging']) cmd += ' && ' # set bridge forward delay - cmd += 'brctl setfd {0} {1}'.format(bridge['br_name'], bridge['forwarding_delay']) + cmd += 'brctl setfd {} {}'.format(bridge['br_name'], bridge['forwarding_delay']) cmd += ' && ' # set hello time - cmd += 'brctl sethello {0} {1}'.format(bridge['br_name'], bridge['hello_time']) + cmd += 'brctl sethello {} {}'.format(bridge['br_name'], bridge['hello_time']) cmd += ' && ' # set max message age - cmd += 'brctl setmaxage {0} {1}'.format(bridge['br_name'], bridge['max_age']) + cmd += 'brctl setmaxage {} {}'.format(bridge['br_name'], bridge['max_age']) cmd += ' && ' # set bridge priority - cmd += 'brctl setbridgeprio {0} {1}'.format(bridge['br_name'], bridge['priority']) + cmd += 'brctl setbridgeprio {} {}'.format(bridge['br_name'], bridge['priority']) cmd += ' && ' # turn stp on/off - cmd += 'brctl stp {0} {1}'.format(bridge['br_name'], bridge['stp']) + cmd += 'brctl stp {} {}'.format(bridge['br_name'], bridge['stp']) + + for intf in bridge['member_remove']: + # remove interface from bridge + cmd += ' && ' + cmd += 'brctl delif {} {}'.format(bridge['br_name'], intf) + + for intf in bridge['member']: + # add interface to bridge + # but only if it is not yet member of this bridge + if not os.path.exists('/sys/devices/virtual/net/' + bridge['br_name'] + '/brif/' + intf['name']): + cmd += ' && ' + cmd += 'brctl addif {} {}'.format(bridge['br_name'], intf['name']) + + # set bridge port cost + if intf['cost']: + cmd += ' && ' + cmd += 'brctl setpathcost {} {} {}'.format(bridge['br_name'], intf['name'], intf['cost']) + + # set bridge port priority + if intf['priority']: + cmd += ' && ' + cmd += 'brctl setportprio {} {} {}'.format(bridge['br_name'], intf['name'], intf['priority']) subprocess_cmd(cmd) + # Change interface MAC address + if bridge['mac']: + VyIfconfig.set_mac_address(bridge['br_name'], bridge['mac']) + else: + print("TODO: change mac mac address to the autoselected one based on member interfaces" + # update interface description used e.g. within SNMP VyIfconfig.set_description(bridge['br_name'], bridge['description']) diff --git a/src/migration-scripts/interface/0-to-1 b/src/migration-scripts/interface/0-to-1 deleted file mode 100755 index 1c6119d86..000000000 --- a/src/migration-scripts/interface/0-to-1 +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python3 - -# Change syntax of bridge interface -# - move interface based bridge-group to actual bridge (de-nest) -# - make stp and igmp-snooping nodes valueless -# https://phabricator.vyos.net/T1556 - -import sys - -from vyos.configtree import ConfigTree - -if (len(sys.argv) < 1): - print("Must specify file name!") - sys.exit(1) - -file_name = sys.argv[1] - -with open(file_name, 'r') as f: - config_file = f.read() - -config = ConfigTree(config_file) -base = ['interfaces', 'bridge'] - -# -# make stp and igmp-snooping nodes valueless -# -for br in config.list_nodes(base): - # STP: check if enabled - stp_val = config.return_value(base + [br, 'stp']) - # STP: delete node with old syntax - config.delete(base + [br, 'stp']) - # STP: set new node - if enabled - if stp_val == "true": - config.set(base + [br, 'stp'], value=None) - - - # igmp-snooping: check if enabled - igmp_val = config.return_value(base + [br, 'igmp-snooping', 'querier']) - # igmp-snooping: delete node with old syntax - config.delete(base + [br, 'igmp-snooping', 'querier']) - # igmp-snooping: set new node - if enabled - if igmp_val == "enable": - config.set(base + [br, 'igmp-snooping', 'querier'], value=None) - -# -# move interface based bridge-group to actual bridge (de-nest) -# -bridge_types = ['bonding', 'ethernet', 'l2tpv3', 'openvpn', 'vxlan', 'wireless'] -for type in bridge_types: - if not config.exists(['interfaces', type]): - continue - - for intf in config.list_nodes(['interfaces', type]): - # check if bridge-group exists - if config.exists(['interfaces', type, intf, 'bridge-group']): - bridge = config.return_value(['interfaces', type, intf, 'bridge-group', 'bridge']) - - # create new bridge member interface - config.set(base + [bridge, 'member', 'interface', intf]) - # format as tag node to avoid loading problems - config.set_tag(base + [bridge, 'member', 'interface']) - - # cost: migrate if configured - if config.exists(['interfaces', type, intf, 'bridge-group', 'cost']): - cost = config.return_value(['interfaces', type, intf, 'bridge-group', 'cost']) - # set new node - config.set(base + [bridge, 'member', 'interface', intf, 'cost'], value=cost) - - if config.exists(['interfaces', type, intf, 'bridge-group', 'priority']): - priority = config.return_value(['interfaces', type, intf, 'bridge-group', 'priority']) - # set new node - config.set(base + [bridge, 'member', 'interface', intf, 'priority'], value=priority) - - # Delete the old bridge-group assigned to an interface - config.delete(['interfaces', type, intf, 'bridge-group']) - - try: - with open(file_name, 'w') as f: - f.write(config.to_string()) - except OSError as e: - print("Failed to save the modified config: {}".format(e)) - sys.exit(1) diff --git a/src/migration-scripts/interfaces/0-to-1 b/src/migration-scripts/interfaces/0-to-1 new file mode 100755 index 000000000..1c6119d86 --- /dev/null +++ b/src/migration-scripts/interfaces/0-to-1 @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 + +# Change syntax of bridge interface +# - move interface based bridge-group to actual bridge (de-nest) +# - make stp and igmp-snooping nodes valueless +# https://phabricator.vyos.net/T1556 + +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + print("Must specify file name!") + sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) +base = ['interfaces', 'bridge'] + +# +# make stp and igmp-snooping nodes valueless +# +for br in config.list_nodes(base): + # STP: check if enabled + stp_val = config.return_value(base + [br, 'stp']) + # STP: delete node with old syntax + config.delete(base + [br, 'stp']) + # STP: set new node - if enabled + if stp_val == "true": + config.set(base + [br, 'stp'], value=None) + + + # igmp-snooping: check if enabled + igmp_val = config.return_value(base + [br, 'igmp-snooping', 'querier']) + # igmp-snooping: delete node with old syntax + config.delete(base + [br, 'igmp-snooping', 'querier']) + # igmp-snooping: set new node - if enabled + if igmp_val == "enable": + config.set(base + [br, 'igmp-snooping', 'querier'], value=None) + +# +# move interface based bridge-group to actual bridge (de-nest) +# +bridge_types = ['bonding', 'ethernet', 'l2tpv3', 'openvpn', 'vxlan', 'wireless'] +for type in bridge_types: + if not config.exists(['interfaces', type]): + continue + + for intf in config.list_nodes(['interfaces', type]): + # check if bridge-group exists + if config.exists(['interfaces', type, intf, 'bridge-group']): + bridge = config.return_value(['interfaces', type, intf, 'bridge-group', 'bridge']) + + # create new bridge member interface + config.set(base + [bridge, 'member', 'interface', intf]) + # format as tag node to avoid loading problems + config.set_tag(base + [bridge, 'member', 'interface']) + + # cost: migrate if configured + if config.exists(['interfaces', type, intf, 'bridge-group', 'cost']): + cost = config.return_value(['interfaces', type, intf, 'bridge-group', 'cost']) + # set new node + config.set(base + [bridge, 'member', 'interface', intf, 'cost'], value=cost) + + if config.exists(['interfaces', type, intf, 'bridge-group', 'priority']): + priority = config.return_value(['interfaces', type, intf, 'bridge-group', 'priority']) + # set new node + config.set(base + [bridge, 'member', 'interface', intf, 'priority'], value=priority) + + # Delete the old bridge-group assigned to an interface + config.delete(['interfaces', type, intf, 'bridge-group']) + + try: + with open(file_name, 'w') as f: + f.write(config.to_string()) + except OSError as e: + print("Failed to save the modified config: {}".format(e)) + sys.exit(1) -- cgit v1.2.3 From 5b15c162c008958de2be9cba2cbf8f0b65bc6fb9 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 3 Aug 2019 22:24:10 +0200 Subject: [bridge] T1156: interfaces can be assigned to any one bridge only --- src/conf_mode/interface-bridge.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src/conf_mode/interface-bridge.py') diff --git a/src/conf_mode/interface-bridge.py b/src/conf_mode/interface-bridge.py index 637c58a5e..1c9c6ec10 100755 --- a/src/conf_mode/interface-bridge.py +++ b/src/conf_mode/interface-bridge.py @@ -174,8 +174,16 @@ def verify(bridge): if bridge is None: return None - # validate agains other bridge interfaces that the interface is not assigned - # to another bridge + conf = Config() + for br in conf.list_nodes('interfaces bridge'): + # it makes no sense to verify ourself in this case + if br == bridge['br_name']: + continue + + for intf in bridge['member']: + tmp = conf.list_nodes('interfaces bridge {} member interface'.format(br)) + if intf['name'] in tmp: + raise ConfigError('{} can be assigned to any one bridge only'.format(intf['name'])) return None -- cgit v1.2.3 From 74cd9b982a3e965d422bce84375f6283088ec593 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 4 Aug 2019 21:50:10 +0200 Subject: [bridge] T1156: rename igmp-snooping node to igmp --- interface-definitions/interfaces-bridge.xml | 6 +++--- src/conf_mode/interface-bridge.py | 4 ++-- src/migration-scripts/interfaces/0-to-1 | 3 +-- 3 files changed, 6 insertions(+), 7 deletions(-) (limited to 'src/conf_mode/interface-bridge.py') diff --git a/interface-definitions/interfaces-bridge.xml b/interface-definitions/interfaces-bridge.xml index af19d9438..16fd8b14c 100644 --- a/interface-definitions/interfaces-bridge.xml +++ b/interface-definitions/interfaces-bridge.xml @@ -139,14 +139,14 @@ Bridge Hello interval must be between 1 and 10 seconds - + - IGMP snooping settings + Internet Group Management Protocol (IGMP) settings - Enable or disable IGMP querier + Enable IGMP querier diff --git a/src/conf_mode/interface-bridge.py b/src/conf_mode/interface-bridge.py index 1c9c6ec10..4f1cbd17c 100755 --- a/src/conf_mode/interface-bridge.py +++ b/src/conf_mode/interface-bridge.py @@ -121,8 +121,8 @@ def get_config(): if conf.exists('hello-time'): bridge['hello_time'] = conf.return_value('hello-time') - # Enable or disable IGMP querier - if conf.exists('igmp-snooping querier'): + # Enable Internet Group Management Protocol (IGMP) querier + if conf.exists('igmp querier'): bridge['igmp_querier'] = 1 # ARP cache entry timeout in seconds diff --git a/src/migration-scripts/interfaces/0-to-1 b/src/migration-scripts/interfaces/0-to-1 index 1c6119d86..b8e190f2c 100755 --- a/src/migration-scripts/interfaces/0-to-1 +++ b/src/migration-scripts/interfaces/0-to-1 @@ -33,14 +33,13 @@ for br in config.list_nodes(base): if stp_val == "true": config.set(base + [br, 'stp'], value=None) - # igmp-snooping: check if enabled igmp_val = config.return_value(base + [br, 'igmp-snooping', 'querier']) # igmp-snooping: delete node with old syntax config.delete(base + [br, 'igmp-snooping', 'querier']) # igmp-snooping: set new node - if enabled if igmp_val == "enable": - config.set(base + [br, 'igmp-snooping', 'querier'], value=None) + config.set(base + [br, 'igmp', 'querier'], value=None) # # move interface based bridge-group to actual bridge (de-nest) -- cgit v1.2.3 From 61cf03e22bbd1cef574e1884e9814cc3cc464a90 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 5 Aug 2019 11:41:39 +0200 Subject: [bridge] T1156: support adding interface addresses --- python/vyos/configinterface.py | 40 +++++++++++++++++++++++++ src/conf_mode/interface-bridge.py | 61 +++++++++++++++++++++++++-------------- 2 files changed, 79 insertions(+), 22 deletions(-) (limited to 'src/conf_mode/interface-bridge.py') diff --git a/python/vyos/configinterface.py b/python/vyos/configinterface.py index 0f25c4a89..0f5b0842c 100644 --- a/python/vyos/configinterface.py +++ b/python/vyos/configinterface.py @@ -14,6 +14,7 @@ # License along with this library. If not, see . import os +import vyos.validate def validate_mac_address(addr): # a mac address consits out of 6 octets @@ -111,3 +112,42 @@ def set_link_detect(intf, enable): os.system('/usr/bin/vtysh -c "configure terminal" -c "interface {}" -c "no link-detect"'.format(intf)) pass + +def add_interface_address(intf, addr): + """ + Configure an interface IPv4/IPv6 address + """ + if addr == "dhcp": + os.system('/opt/vyatta/sbin/vyatta-interfaces.pl --dev="{}" --dhcp=start'.format(intf)) + elif addr == "dhcpv6": + os.system('/opt/vyatta/sbin/vyatta-dhcpv6-client.pl --start -ifname "{}"'.format(intf)) + elif vyos.validate.is_ipv4(addr): + if not vyos.validate.is_intf_addr_assigned(intf, addr): + print("Assigning {} to {}".format(addr, intf)) + os.system('sudo ip -4 addr add "{}" broadcast + dev "{}"'.format(addr, intf)) + elif vyos.validate.is_ipv6(addr): + if not vyos.validate.is_intf_addr_assigned(intf, addr): + print("Assigning {} to {}".format(addr, intf)) + os.system('sudo ip -6 addr add "{}" dev "{}"'.format(addr, intf)) + else: + raise ConfigError('{} is not a valid interface address'.format(addr)) + + pass + +def remove_interface_address(intf, addr): + """ + Remove IPv4/IPv6 address from given interface + """ + + if addr == "dhcp": + os.system('/opt/vyatta/sbin/vyatta-interfaces.pl --dev="{}" --dhcp=stop'.format(intf)) + elif addr == "dhcpv6": + os.system('/opt/vyatta/sbin/vyatta-dhcpv6-client.pl --stop -ifname "{}"'.format(intf)) + elif vyos.validate.is_ipv4(addr): + os.system('ip -4 addr del "{}" dev "{}"'.format(addr, intf)) + elif vyos.validate.is_ipv6(addr): + os.system('ip -6 addr del "{}" dev "{}"'.format(addr, intf)) + else: + raise ConfigError('{} is not a valid interface address'.format(addr)) + + pass diff --git a/src/conf_mode/interface-bridge.py b/src/conf_mode/interface-bridge.py index 4f1cbd17c..93eb3839c 100755 --- a/src/conf_mode/interface-bridge.py +++ b/src/conf_mode/interface-bridge.py @@ -28,6 +28,7 @@ from vyos import ConfigError default_config_data = { 'address': [], + 'address_remove': [], 'aging': '300', 'br_name': '', 'description': '', @@ -53,7 +54,7 @@ default_config_data = { def subprocess_cmd(command): process = subprocess.Popen(command,stdout=subprocess.PIPE, shell=True) proc_stdout = process.communicate()[0].strip() - print(proc_stdout) + pass def diff(first, second): second = set(second) @@ -154,12 +155,18 @@ def get_config(): bridge['member'].append(iface) - # Determine bridge member interface (currently effective) - to determine which interfaces - # need to be removed from the bridge + # Determine bridge member interface (currently effective) - to determine which + # interfaces is no longer assigend to the bridge and thus can be removed eff_intf = conf.list_effective_nodes('member interface') act_intf = conf.list_nodes('member interface') bridge['member_remove'] = diff(eff_intf, act_intf) + # Determine interface addresses (currently effective) - to determine which + # address is no longer valid and needs to be removed from the bridge + eff_addr = conf.return_effective_values('address') + act_addr = conf.return_values('address') + bridge['address_remove'] = diff(eff_addr, act_addr) + # Priority for this bridge if conf.exists('priority'): bridge['priority'] = conf.return_value('priority') @@ -197,72 +204,75 @@ def apply(bridge): if bridge is None: return None + cmd = '' if bridge['deleted']: # bridges need to be shutdown first - os.system("ip link set dev {} down".format(bridge['br_name'])) + cmd += 'ip link set dev "{}" down'.format(bridge['br_name']) + cmd += ' && ' # delete bridge - os.system("brctl delbr {}".format(bridge['br_name'])) + cmd += 'brctl delbr "{}"'.format(bridge['br_name']) + subprocess_cmd(cmd) + else: # create bridge if it does not exist if not os.path.exists("/sys/class/net/" + bridge['br_name']): - os.system("brctl addbr {}".format(bridge['br_name'])) - - # assemble bridge configuration - # configuration is passed via subprocess to brctl - cmd = '' + # create bridge interface + cmd += 'brctl addbr "{}"'.format(bridge['br_name']) + cmd += ' && ' + # activate "UP" the interface + cmd += 'ip link set dev "{}" up'.format(bridge['br_name']) + cmd += ' && ' # set ageing time - cmd += 'brctl setageing {} {}'.format(bridge['br_name'], bridge['aging']) + cmd += 'brctl setageing "{}" "{}"'.format(bridge['br_name'], bridge['aging']) cmd += ' && ' # set bridge forward delay - cmd += 'brctl setfd {} {}'.format(bridge['br_name'], bridge['forwarding_delay']) + cmd += 'brctl setfd "{}" "{}"'.format(bridge['br_name'], bridge['forwarding_delay']) cmd += ' && ' # set hello time - cmd += 'brctl sethello {} {}'.format(bridge['br_name'], bridge['hello_time']) + cmd += 'brctl sethello "{}" "{}"'.format(bridge['br_name'], bridge['hello_time']) cmd += ' && ' # set max message age - cmd += 'brctl setmaxage {} {}'.format(bridge['br_name'], bridge['max_age']) + cmd += 'brctl setmaxage "{}" "{}"'.format(bridge['br_name'], bridge['max_age']) cmd += ' && ' # set bridge priority - cmd += 'brctl setbridgeprio {} {}'.format(bridge['br_name'], bridge['priority']) + cmd += 'brctl setbridgeprio "{}" "{}"'.format(bridge['br_name'], bridge['priority']) cmd += ' && ' # turn stp on/off - cmd += 'brctl stp {} {}'.format(bridge['br_name'], bridge['stp']) + cmd += 'brctl stp "{}" "{}"'.format(bridge['br_name'], bridge['stp']) for intf in bridge['member_remove']: # remove interface from bridge cmd += ' && ' - cmd += 'brctl delif {} {}'.format(bridge['br_name'], intf) + cmd += 'brctl delif "{}" "{}"'.format(bridge['br_name'], intf) for intf in bridge['member']: # add interface to bridge # but only if it is not yet member of this bridge if not os.path.exists('/sys/devices/virtual/net/' + bridge['br_name'] + '/brif/' + intf['name']): cmd += ' && ' - cmd += 'brctl addif {} {}'.format(bridge['br_name'], intf['name']) + cmd += 'brctl addif "{}" "{}"'.format(bridge['br_name'], intf['name']) # set bridge port cost if intf['cost']: cmd += ' && ' - cmd += 'brctl setpathcost {} {} {}'.format(bridge['br_name'], intf['name'], intf['cost']) + cmd += 'brctl setpathcost "{}" "{}" "{}"'.format(bridge['br_name'], intf['name'], intf['cost']) # set bridge port priority if intf['priority']: cmd += ' && ' - cmd += 'brctl setportprio {} {} {}'.format(bridge['br_name'], intf['name'], intf['priority']) + cmd += 'brctl setportprio "{}" "{}" "{}"'.format(bridge['br_name'], intf['name'], intf['priority']) subprocess_cmd(cmd) # Change interface MAC address if bridge['mac']: VyIfconfig.set_mac_address(bridge['br_name'], bridge['mac']) - else: - print("TODO: change mac mac address to the autoselected one based on member interfaces" # update interface description used e.g. within SNMP VyIfconfig.set_description(bridge['br_name'], bridge['description']) @@ -276,6 +286,13 @@ def apply(bridge): # ARP cache entry timeout in seconds VyIfconfig.set_arp_cache_timeout(bridge['br_name'], bridge['arp_cache_timeout_ms']) + # Configure interface address(es) + for addr in bridge['address_remove']: + VyIfconfig.remove_interface_address(bridge['br_name'], addr) + + for addr in bridge['address']: + VyIfconfig.add_interface_address(bridge['br_name'], addr) + return None if __name__ == '__main__': -- cgit v1.2.3 From a7d5e9d23ab62829781c431d243f4b93c59b28a5 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 7 Aug 2019 10:23:50 +0200 Subject: [bridge] T1156: rename 'br_name' to 'intf' for indexing python dictionary interface name --- src/conf_mode/interface-bridge.py | 58 +++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 29 deletions(-) (limited to 'src/conf_mode/interface-bridge.py') diff --git a/src/conf_mode/interface-bridge.py b/src/conf_mode/interface-bridge.py index 93eb3839c..543349e7b 100755 --- a/src/conf_mode/interface-bridge.py +++ b/src/conf_mode/interface-bridge.py @@ -30,7 +30,7 @@ default_config_data = { 'address': [], 'address_remove': [], 'aging': '300', - 'br_name': '', + 'arp_cache_timeout_ms': '30000', 'description': '', 'deleted': False, 'dhcp_client_id': '', @@ -42,7 +42,7 @@ default_config_data = { 'forwarding_delay': '15', 'hello_time': '2', 'igmp_querier': 0, - 'arp_cache_timeout_ms': '30000', + 'intf': '', 'mac' : '', 'max_age': '20', 'member': [], @@ -66,17 +66,17 @@ def get_config(): # determine tagNode instance try: - bridge['br_name'] = os.environ['VYOS_TAGNODE_VALUE'] + bridge['intf'] = os.environ['VYOS_TAGNODE_VALUE'] except KeyError as E: print("Interface not specified") # Check if bridge has been removed - if not conf.exists('interfaces bridge ' + bridge['br_name']): + if not conf.exists('interfaces bridge ' + bridge['intf']): bridge['deleted'] = True return bridge # set new configuration level - conf.set_level('interfaces bridge ' + bridge['br_name']) + conf.set_level('interfaces bridge ' + bridge['intf']) # retrieve configured interface addresses if conf.exists('address'): @@ -184,7 +184,7 @@ def verify(bridge): conf = Config() for br in conf.list_nodes('interfaces bridge'): # it makes no sense to verify ourself in this case - if br == bridge['br_name']: + if br == bridge['intf']: continue for intf in bridge['member']: @@ -207,91 +207,91 @@ def apply(bridge): cmd = '' if bridge['deleted']: # bridges need to be shutdown first - cmd += 'ip link set dev "{}" down'.format(bridge['br_name']) + cmd += 'ip link set dev "{}" down'.format(bridge['intf']) cmd += ' && ' # delete bridge - cmd += 'brctl delbr "{}"'.format(bridge['br_name']) + cmd += 'brctl delbr "{}"'.format(bridge['intf']) subprocess_cmd(cmd) else: # create bridge if it does not exist - if not os.path.exists("/sys/class/net/" + bridge['br_name']): + if not os.path.exists("/sys/class/net/" + bridge['intf']): # create bridge interface - cmd += 'brctl addbr "{}"'.format(bridge['br_name']) + cmd += 'brctl addbr "{}"'.format(bridge['intf']) cmd += ' && ' # activate "UP" the interface - cmd += 'ip link set dev "{}" up'.format(bridge['br_name']) + cmd += 'ip link set dev "{}" up'.format(bridge['intf']) cmd += ' && ' # set ageing time - cmd += 'brctl setageing "{}" "{}"'.format(bridge['br_name'], bridge['aging']) + cmd += 'brctl setageing "{}" "{}"'.format(bridge['intf'], bridge['aging']) cmd += ' && ' # set bridge forward delay - cmd += 'brctl setfd "{}" "{}"'.format(bridge['br_name'], bridge['forwarding_delay']) + cmd += 'brctl setfd "{}" "{}"'.format(bridge['intf'], bridge['forwarding_delay']) cmd += ' && ' # set hello time - cmd += 'brctl sethello "{}" "{}"'.format(bridge['br_name'], bridge['hello_time']) + cmd += 'brctl sethello "{}" "{}"'.format(bridge['intf'], bridge['hello_time']) cmd += ' && ' # set max message age - cmd += 'brctl setmaxage "{}" "{}"'.format(bridge['br_name'], bridge['max_age']) + cmd += 'brctl setmaxage "{}" "{}"'.format(bridge['intf'], bridge['max_age']) cmd += ' && ' # set bridge priority - cmd += 'brctl setbridgeprio "{}" "{}"'.format(bridge['br_name'], bridge['priority']) + cmd += 'brctl setbridgeprio "{}" "{}"'.format(bridge['intf'], bridge['priority']) cmd += ' && ' # turn stp on/off - cmd += 'brctl stp "{}" "{}"'.format(bridge['br_name'], bridge['stp']) + cmd += 'brctl stp "{}" "{}"'.format(bridge['intf'], bridge['stp']) for intf in bridge['member_remove']: # remove interface from bridge cmd += ' && ' - cmd += 'brctl delif "{}" "{}"'.format(bridge['br_name'], intf) + cmd += 'brctl delif "{}" "{}"'.format(bridge['intf'], intf) for intf in bridge['member']: # add interface to bridge # but only if it is not yet member of this bridge - if not os.path.exists('/sys/devices/virtual/net/' + bridge['br_name'] + '/brif/' + intf['name']): + if not os.path.exists('/sys/devices/virtual/net/' + bridge['intf'] + '/brif/' + intf['name']): cmd += ' && ' - cmd += 'brctl addif "{}" "{}"'.format(bridge['br_name'], intf['name']) + cmd += 'brctl addif "{}" "{}"'.format(bridge['intf'], intf['name']) # set bridge port cost if intf['cost']: cmd += ' && ' - cmd += 'brctl setpathcost "{}" "{}" "{}"'.format(bridge['br_name'], intf['name'], intf['cost']) + cmd += 'brctl setpathcost "{}" "{}" "{}"'.format(bridge['intf'], intf['name'], intf['cost']) # set bridge port priority if intf['priority']: cmd += ' && ' - cmd += 'brctl setportprio "{}" "{}" "{}"'.format(bridge['br_name'], intf['name'], intf['priority']) + cmd += 'brctl setportprio "{}" "{}" "{}"'.format(bridge['intf'], intf['name'], intf['priority']) subprocess_cmd(cmd) # Change interface MAC address if bridge['mac']: - VyIfconfig.set_mac_address(bridge['br_name'], bridge['mac']) + VyIfconfig.set_mac_address(bridge['intf'], bridge['mac']) # update interface description used e.g. within SNMP - VyIfconfig.set_description(bridge['br_name'], bridge['description']) + VyIfconfig.set_description(bridge['intf'], bridge['description']) # Ignore link state changes? - VyIfconfig.set_link_detect(bridge['br_name'], bridge['disable_link_detect']) + VyIfconfig.set_link_detect(bridge['intf'], bridge['disable_link_detect']) # enable or disable IGMP querier - VyIfconfig.set_multicast_querier(bridge['br_name'], bridge['igmp_querier']) + VyIfconfig.set_multicast_querier(bridge['intf'], bridge['igmp_querier']) # ARP cache entry timeout in seconds - VyIfconfig.set_arp_cache_timeout(bridge['br_name'], bridge['arp_cache_timeout_ms']) + VyIfconfig.set_arp_cache_timeout(bridge['intf'], bridge['arp_cache_timeout_ms']) # Configure interface address(es) for addr in bridge['address_remove']: - VyIfconfig.remove_interface_address(bridge['br_name'], addr) + VyIfconfig.remove_interface_address(bridge['intf'], addr) for addr in bridge['address']: - VyIfconfig.add_interface_address(bridge['br_name'], addr) + VyIfconfig.add_interface_address(bridge['intf'], addr) return None -- cgit v1.2.3