diff options
Diffstat (limited to 'python')
-rw-r--r-- | python/vyos/ifconfig.py | 865 |
1 files changed, 682 insertions, 183 deletions
diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index 5f28125af..944c1ef82 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -1,5 +1,3 @@ -#!/usr/bin/python3 - # Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or @@ -17,49 +15,80 @@ import sys import os -import re -import json -import socket import subprocess import ipaddress +import jinja2 from vyos.validate import * from ipaddress import IPv4Network, IPv6Address from netifaces import ifaddresses, AF_INET, AF_INET6 - -dhclient_conf_dir = r'/var/lib/dhcp/dhclient_' +from time import sleep + +dhcp_cfg = """ +# generated by ifconfig.py +option rfc3442-classless-static-routes code 121 = array of unsigned integer 8; +interface "{{ intf }}" { + send host-name "{{ hostname }}"; + request subnet-mask, broadcast-address, routers, domain-name-servers, rfc3442-classless-static-routes, domain-name, interface-mtu; +} +""" + +dhcpv6_cfg = """ +# generated by ifconfig.py +interface "{{ intf }}" { + request routers, domain-name-servers, domain-name; +} +""" + +dhclient_base = r'/var/lib/dhcp/dhclient_' class Interface: - def __init__(self, ifname=None, type=None): + def __init__(self, ifname=None, type=None, debug=False): """ Create instance of an IP interface Example: >>> from vyos.ifconfig import Interface - >>> i = Interface('br111', type='bridge') + >>> i = Interface('eth0') """ if not ifname: - raise Exception("interface name required") - - if not os.path.exists('/sys/class/net/{0}'.format(ifname)) and not type: - raise Exception("interface {0} not found".format(str(ifname))) - - if not os.path.exists('/sys/class/net/{0}'.format(ifname)): - try: - cmd = 'ip link add dev "{}" type "{}"'.format(ifname, type) - self._cmd(cmd) - except subprocess.CalledProcessError as e: - if self._debug(): - self._debug(e) - if "Operation not supported" in str(e.output.decode()): - print(str(e.output.decode())) - sys.exit(0) + raise Exception('interface name required') + + if not os.path.exists('/sys/class/net/{}'.format(ifname)) and not type: + raise Exception('interface "{}" not found'.format(str(ifname))) + # variable already referenced from _debug() + self._debug = debug self._ifname = str(ifname) - @property + if not os.path.exists('/sys/class/net/{}'.format(ifname)): + cmd = 'ip link add dev "{}" type "{}"'.format(ifname, type) + self._cmd(cmd) + + # per interface DHCP config files + self._dhcp_cfg_file = dhclient_base + self._ifname + '.conf' + self._dhcp_pid_file = dhclient_base + self._ifname + '.pid' + self._dhcp_lease_file = dhclient_base + self._ifname + '.leases' + + # per interface DHCPv6 config files + self._dhcpv6_cfg_file = dhclient_base + self._ifname + '.v6conf' + self._dhcpv6_pid_file = dhclient_base + self._ifname + '.v6pid' + self._dhcpv6_lease_file = dhclient_base + self._ifname + '.v6leases' + + + def _debug_msg(self, msg): + if self._debug: + print('"DEBUG/{}: {}'.format(self._ifname, msg)) + + + def set_debug(self, debug): + if debug not in [True, False]: + raise ValueError('must specify True or False for debug') + self._debug = debug + + def remove(self): """ Remove system interface @@ -67,21 +96,28 @@ class Interface: Example: >>> from vyos.ifconfig import Interface - >>> i = Interface('br111', type='bridge') - >>> i.remove + >>> i = Interface('eth0') + >>> i.remove() """ + # stop DHCP(v6) if running + self.del_dhcp() + self.del_dhcpv6() + # NOTE (Improvement): # after interface removal no other commands should be allowed # to be called and instead should raise an Exception: - cmd = 'ip link del dev "{}"'.format(self._ifname) self._cmd(cmd) def _cmd(self, command): + self._debug_msg(command) + process = subprocess.Popen(command,stdout=subprocess.PIPE, shell=True) proc_stdout = process.communicate()[0].strip() + + # add exception handling code pass @@ -93,7 +129,7 @@ class Interface: Example: >>> from vyos.ifconfig import Interface - >>> Interface('eth1').mtu + >>> Interface('eth0').mtu '1500' """ @@ -111,8 +147,8 @@ class Interface: Example: >>> from vyos.ifconfig import Interface - >>> Interface('br100', type='bridge').mtu = 1400 - >>> Interface('br100').mtu + >>> Interface('eth0').mtu = 1400 + >>> Interface('eth0').mtu '1400' """ @@ -131,7 +167,7 @@ class Interface: Example: >>> from vyos.ifconfig import Interface - >>> Interface('eth1').mac + >>> Interface('eth0').mac '00:0c:29:11:aa:cc' """ address = '' @@ -148,8 +184,8 @@ class Interface: Example: >>> from vyos.ifconfig import Interface - >>> Interface('eth1').mac = '00:90:43:fe:fe:1b' - >>> Interface('eth1').mac + >>> Interface('eth0').mac = '00:90:43:fe:fe:1b' + >>> Interface('eth0').mac '00:90:43:fe:fe:1b' """ # a mac address consits out of 6 octets @@ -176,6 +212,97 @@ class Interface: @property + def arp_cache_tmo(self): + """ + Get configured ARP cache timeout value from interface. Example shows + default value of 30 seconds. + + Example: + + >>> from vyos.ifconfig import Interface + >>> Interface('eth0').arp_cache_tmo + '30000' + """ + + alias = '' + with open('/proc/sys/net/ipv4/neigh/{0}/base_reachable_time_ms'.format(self._ifname), 'r') as f: + alias = f.read().rstrip('\n') + return alias + + + @arp_cache_tmo.setter + def arp_cache_tmo(self, tmo=None): + """ + Set ARP cache timeout value in seconds for this. + + Example: + + >>> from vyos.ifconfig import Interface + >>> Interface('eth0').arp_cache_tmo = '40000' + """ + + # clear interface alias + if not tmo: + raise ValueError('Timeout value required') + + # Kernel interface is on milli seconds + tmo = int(tmo) * 1000 + with open('/proc/sys/net/ipv4/neigh/{0}/base_reachable_time_ms'.format(self._ifname), 'w') as f: + f.write(str(tmo)) + + @property + def link_detect(self): + """ + How does the kernel act when receiving packets on 'down' interfaces + + Example: + + >>> from vyos.ifconfig import Interface + >>> Interface('eth0').link_detect + '0' + """ + + alias = '' + with open('/proc/sys/net/ipv4/conf/{0}/link_filter'.format(self._ifname), 'r') as f: + alias = f.read().rstrip('\n') + return alias + + + @link_detect.setter + def link_detect(self, link_filter=None): + """ + Konfigure kernel response in packets received on interfaces that are 'down' + + 0 - Allow packets to be received for the address on this interface + even if interface is disabled or no carrier. + + 1 - Ignore packets received if interface associated with the incoming + address is down. + + 2 - Ignore packets received if interface associated with the incoming + address is down or has no carrier. + + Default value is 0. Note that some distributions enable it in startup + scripts. + + Example: + + >>> from vyos.ifconfig import Interface + >>> Interface('eth0').link_detect = '1' + """ + + # clear interface alias + if not link_filter: + raise ValueError() + + if link_filter >= 0 and link_filter <= 2: + with open('/proc/sys/net/ipv4/conf/{0}/link_filter'.format(self._ifname), 'w') as f: + f.write(str(link_filter)) + else: + raise ValueError() + + + @property def ifalias(self): """ Get/set interface alias name @@ -183,7 +310,7 @@ class Interface: Example: >>> from vyos.ifconfig import Interface - >>> Interface('eth1').ifalias + >>> Interface('eth0').ifalias '' """ @@ -201,14 +328,14 @@ class Interface: Example: >>> from vyos.ifconfig import Interface - >>> Interface('eth1').ifalias = 'VyOS upstream interface' - >>> Interface('eth1').ifalias + >>> Interface('eth0').ifalias = 'VyOS upstream interface' + >>> Interface('eth0').ifalias 'VyOS upstream interface' to clear interface alias e.g. delete it use: - >>> Interface('eth1').ifalias = '' - >>> Interface('eth1').ifalias + >>> Interface('eth0').ifalias = '' + >>> Interface('eth0').ifalias '' """ @@ -219,6 +346,7 @@ class Interface: with open('/sys/class/net/{0}/ifalias'.format(self._ifname), 'w') as f: f.write(str(ifalias)) + @property def state(self): """ @@ -227,7 +355,7 @@ class Interface: Example: >>> from vyos.ifconfig import Interface - >>> Interface('eth1').state + >>> Interface('eth0').state 'up' """ @@ -245,8 +373,8 @@ class Interface: Example: >>> from vyos.ifconfig import Interface - >>> Interface('eth1').state = 'down' - >>> Interface('eth1').state + >>> Interface('eth0').state = 'down' + >>> Interface('eth0').state 'down' """ @@ -259,18 +387,6 @@ class Interface: self._cmd(cmd) - def _debug(self, e=None): - """ - export DEBUG=1 to see debug messages - """ - if os.getenv('DEBUG') == '1': - if e: - print ("Exception raised:\ncommand: {0}\nerror code: {1}\nsubprocess output: {2}".format( - e.cmd, e.returncode, e.output.decode())) - return True - return False - - def get_addr(self): """ Retrieve assigned IPv4 and IPv6 addresses from given interface. @@ -279,7 +395,7 @@ class Interface: Example: >>> from vyos.ifconfig import Interface - >>> Interface('eth1').get_addrs() + >>> Interface('eth0').get_addrs() ['172.16.33.30/24', 'fe80::20c:29ff:fe11:a174/64'] """ @@ -314,8 +430,8 @@ class Interface: Example: - >>> from vyos.interfaceconfig import Interface - >>> j = Interface('br100', type='bridge') + >>> from vyos.ifconfig import Interface + >>> j = Interface('eth0') >>> j.add_addr('192.0.2.1/24') >>> j.add_addr('2001:db8::ffff/64') >>> j.get_addr() @@ -326,12 +442,7 @@ class Interface: raise ValueError('No IP address specified') if not is_intf_addr_assigned(self._ifname, addr): - cmd = '' - if is_ipv4(addr): - cmd = 'sudo ip -4 addr add "{}" broadcast + dev "{}"'.format(addr, self._ifname) - elif is_ipv6(addr): - cmd = 'sudo ip -6 addr add "{}" dev "{}"'.format(addr, self._ifname) - + cmd = 'sudo ip addr add "{}" dev "{}"'.format(addr, self._ifname) self._cmd(cmd) @@ -340,8 +451,9 @@ class Interface: Remove IP address from interface. Example: - >>> from vyos.interfaceconfig import Interface - >>> j = Interface('br100', type='bridge') + + >>> from vyos.ifconfig import Interface + >>> j = Interface('eth0') >>> j.add_addr('2001:db8::ffff/64') >>> j.add_addr('192.0.2.1/24') >>> j.get_addr() @@ -355,137 +467,524 @@ class Interface: raise ValueError('No IP address specified') if is_intf_addr_assigned(self._ifname, addr): - cmd = '' - if is_ipv4(addr): - cmd = 'ip -4 addr del "{}" dev "{}"'.format(addr, self._ifname) - elif is_ipv6(addr): - cmd = 'ip -6 addr del "{}" dev "{}"'.format(addr, self._ifname) - + cmd = 'ip addr del "{}" dev "{}"'.format(addr, self._ifname) self._cmd(cmd) # replace dhcpv4/v6 with systemd.networkd? - def set_dhcpv4(self): - conf_file = dhclient_conf_dir + self._ifname + '.conf' - pidfile = dhclient_conf_dir + self._ifname + '.pid' - leasefile = dhclient_conf_dir + self._ifname + '.leases' - - a = [ - '# generated by interface_config.py', - 'option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;', - 'interface \"' + self._ifname + '\" {', - '\tsend host-name \"' + socket.gethostname() + '\";', - '\trequest subnet-mask, broadcast-address, routers, domain-name-servers, rfc3442-classless-static-routes, domain-name, interface-mtu;', - '}' - ] - - cnf = "" - for ln in a: - cnf += str(ln + "\n") - open(conf_file, 'w').write(cnf) - if os.path.exists(dhclient_conf_dir + self._ifname + '.pid'): - try: - ret = subprocess.check_output( - ['/sbin/dhclient -4 -r -pf ' + pidfile], shell=True).decode() - except subprocess.CalledProcessError as e: - if self._debug(): - self._debug(e) - try: - ret = subprocess.check_output( - ['/sbin/dhclient -4 -q -nw -cf ' + conf_file + ' -pf ' + pidfile + ' -lf ' + leasefile + ' ' + self._ifname], shell=True).decode() - return True - except subprocess.CalledProcessError as e: - if self._debug(): - self._debug(e) - return None + def set_dhcp(self): + """ + Configure interface as DHCP client. The dhclient binary is automatically + started in background! - def del_dhcpv4(self): - conf_file = dhclient_conf_dir + self._ifname + '.conf' - pidfile = dhclient_conf_dir + self._ifname + '.pid' - leasefile = dhclient_conf_dir + self._ifname + '.leases' - if not os.path.exists(pidfile): - return 1 - try: - ret = subprocess.check_output( - ['/sbin/dhclient -4 -r -pf ' + pidfile], shell=True).decode() - return True - except subprocess.CalledProcessError as e: - if self._debug(): - self._debug(e) - return None + Example: + + >>> from vyos.ifconfig import Interface + >>> j = Interface('eth0') + >>> j.set_dhcp() + """ + + dhcp = { + 'hostname': 'vyos', + 'intf': self._ifname + } + + # read configured system hostname. + # maybe change to vyos hostd client ??? + with open('/etc/hostname', 'r') as f: + dhcp['hostname'] = f.read().rstrip('\n') + + # render DHCP configuration + tmpl = jinja2.Template(dhcp_cfg) + dhcp_text = tmpl.render(dhcp) + with open(self._dhcp_cfg_file, 'w') as f: + f.write(dhcp_text) + + cmd = 'start-stop-daemon --start --quiet --pidfile ' + self._dhcp_pid_file + cmd += ' --exec /sbin/dhclient --' + # now pass arguments to dhclient binary + cmd += ' -4 -nw -cf {} -pf {} -lf {} {}'.format(self._dhcp_cfg_file, self._dhcp_pid_file, self._dhcp_lease_file, self._ifname) + self._cmd(cmd) + + + def del_dhcp(self): + """ + De-configure interface as DHCP clinet. All auto generated files like + pid, config and lease will be removed. + + Example: - def get_dhcpv4(self): - pidfile = dhclient_conf_dir + self._ifname + '.pid' - if not os.path.exists(pidfile): - print ( - "no dhcp client running on interface {0}".format(self._ifname)) - return False + >>> from vyos.ifconfig import Interface + >>> j = Interface('eth0') + >>> j.del_dhcp() + """ + + pid = 0 + if os.path.isfile(self._dhcp_pid_file): + with open(self._dhcp_pid_file, 'r') as f: + pid = int(f.read()) else: - pid = open(pidfile, 'r').read() - print( - "dhclient running on {0} with pid {1}".format(self._ifname, pid)) - return True + self._debug_msg('No DHCP client PID found') + return None + + # stop dhclient + cmd = 'start-stop-daemon --stop --quiet --pidfile {}'.format(self._dhcp_pid_file) + self._cmd(cmd) + + # cleanup old config file + if os.path.isfile(self._dhcp_cfg_file): + os.remove(self._dhcp_cfg_file) + + # cleanup old pid file + if os.path.isfile(self._dhcp_pid_file): + os.remove(self._dhcp_pid_file) + + # cleanup old lease file + if os.path.isfile(self._dhcp_lease_file): + os.remove(self._dhcp_lease_file) + def set_dhcpv6(self): - conf_file = dhclient_conf_dir + self._ifname + '.v6conf' - pidfile = dhclient_conf_dir + self._ifname + '.v6pid' - leasefile = dhclient_conf_dir + self._ifname + '.v6leases' - a = [ - '# generated by interface_config.py', - 'interface \"' + self._ifname + '\" {', - '\trequest routers, domain-name-servers, domain-name;', - '}' - ] - cnf = "" - for ln in a: - cnf += str(ln + "\n") - open(conf_file, 'w').write(cnf) - subprocess.call( - ['sysctl', '-q', '-w', 'net.ipv6.conf.' + self._ifname + '.accept_ra=0']) - if os.path.exists(pidfile): - try: - ret = subprocess.check_output( - ['/sbin/dhclient -6 -q -x -pf ' + pidfile], shell=True).decode() - except subprocess.CalledProcessError as e: - if self._debug(): - self._debug(e) - try: - ret = subprocess.check_output( - ['/sbin/dhclient -6 -q -nw -cf ' + conf_file + ' -pf ' + pidfile + ' -lf ' + leasefile + ' ' + self._ifname], shell=True).decode() - return True - except subprocess.CalledProcessError as e: - if self._debug(): - self._debug(e) - return None + """ + Configure interface as DHCPv6 client. The dhclient binary is automatically + started in background! + + Example: + + >>> from vyos.ifconfig import Interface + >>> j = Interface('eth0') + >>> j.set_dhcpv6() + """ + + dhcpv6 = { + 'intf': self._ifname + } + + # render DHCP configuration + tmpl = jinja2.Template(dhcpv6_cfg) + dhcpv6_text = tmpl.render(dhcpv6) + with open(self._dhcpv6_cfg_file, 'w') as f: + f.write(dhcpv6_text) + + # https://bugs.launchpad.net/ubuntu/+source/ifupdown/+bug/1447715 + # + # wee need to wait for IPv6 DAD to finish once and interface is added + # this suxx :-( + sleep(5) + + # no longer accept router announcements on this interface + cmd = 'sysctl -q -w net.ipv6.conf.{}.accept_ra=0'.format(self._ifname) + self._cmd(cmd) + + cmd = 'start-stop-daemon --start --quiet --pidfile ' + self._dhcpv6_pid_file + cmd += ' --exec /sbin/dhclient --' + # now pass arguments to dhclient binary + cmd += ' -6 -nw -cf {} -pf {} -lf {} {}'.format(self._dhcpv6_cfg_file, self._dhcpv6_pid_file, self._dhcpv6_lease_file, self._ifname) + self._cmd(cmd) + def del_dhcpv6(self): - conf_file = dhclient_conf_dir + self._ifname + '.v6conf' - pidfile = dhclient_conf_dir + self._ifname + '.v6pid' - leasefile = dhclient_conf_dir + self._ifname + '.v6leases' - if not os.path.exists(pidfile): - return 1 - try: - ret = subprocess.check_output( - ['/sbin/dhclient -6 -q -x -pf ' + pidfile], shell=True).decode() - subprocess.call( - ['sysctl', '-q', '-w', 'net.ipv6.conf.' + self._ifname + '.accept_ra=1']) - return True - except subprocess.CalledProcessError as e: - if self._debug(): - self._debug(e) + """ + De-configure interface as DHCPv6 clinet. All auto generated files like + pid, config and lease will be removed. + + Example: + + >>> from vyos.ifconfig import Interface + >>> j = Interface('eth0') + >>> j.del_dhcpv6() + """ + + pid = 0 + if os.path.isfile(self._dhcpv6_pid_file): + with open(self._dhcpv6_pid_file, 'r') as f: + pid = int(f.read()) + else: + self._debug_msg('No DHCPv6 client PID found') return None - def get_dhcpv6(self): - pidfile = dhclient_conf_dir + self._ifname + '.v6pid' - if not os.path.exists(pidfile): - print ( - "no dhcpv6 client running on interface {0}".format(self._ifname)) - return False + # stop dhclient + cmd = 'start-stop-daemon --stop --quiet --pidfile {}'.format(self._dhcpv6_pid_file) + self._cmd(cmd) + + # accept router announcements on this interface + cmd = 'sysctl -q -w net.ipv6.conf.{}.accept_ra=1'.format(self._ifname) + self._cmd(cmd) + + # cleanup old config file + if os.path.isfile(self._dhcpv6_cfg_file): + os.remove(self._dhcpv6_cfg_file) + + # cleanup old pid file + if os.path.isfile(self._dhcpv6_pid_file): + os.remove(self._dhcpv6_pid_file) + + # cleanup old lease file + if os.path.isfile(self._dhcpv6_lease_file): + os.remove(self._dhcpv6_lease_file) + + +class LoopbackIf(Interface): + def __init__(self, ifname=None): + super().__init__(ifname, type='loopback') + + +class DummyIf(Interface): + def __init__(self, ifname=None): + super().__init__(ifname, type='dummy') + + +class BridgeIf(Interface): + def __init__(self, ifname=None): + super().__init__(ifname, type='bridge') + + @property + def ageing_time(self): + """ + Get bridge aging time in seconds. + + Example: + + >>> from vyos.ifconfig import Interface + >>> BridgeIf('br0').aging_time + '3' + """ + + time = 0 + with open('/sys/class/net/{0}/bridge/ageing_time'.format(self._ifname), 'r') as f: + time = int(f.read().rstrip('\n')) + + # kernel representation is in centiseconds - convert to seconds + return time/100 + + + @ageing_time.setter + def ageing_time(self, time=None): + """ + Set bridge aging time in seconds. + + Example: + + >>> from vyos.ifconfig import Interface + >>> BridgeIf('br0').ageing_time = 2 + """ + + if not time: + raise ValueError() + + # kernel representation is in centiseconds - convert from seconds to centiseconds + time = int(time) * 100 + + with open('/sys/class/net/{0}/bridge/ageing_time'.format(self._ifname), 'w') as f: + f.write(str(time)) + + @property + def forward_delay(self): + """ + Get bridge forwarding delay in seconds. + + Example: + + >>> from vyos.ifconfig import Interface + >>> BridgeIf('br0').ageing_time + '3""" + + time = 0 + with open('/sys/class/net/{0}/bridge/forward_delay'.format(self._ifname), 'r') as f: + time = int(f.read().rstrip('\n')) + + # kernel representation is in centiseconds - convert to seconds + return time/100 + + + @forward_delay.setter + def forward_delay(self, time=None): + """ + Set bridge forwarding delay in seconds. + + Example: + + >>> from vyos.ifconfig import Interface + >>> BridgeIf('br0').forward_delay = 15 + """ + + if not time: + raise ValueError() + + # kernel representation is in centiseconds - convert from seconds to centiseconds + time = int(time) * 100 + + with open('/sys/class/net/{0}/bridge/forward_delay'.format(self._ifname), 'w') as f: + f.write(str(time)) + + @property + def hello_time(self): + """ + Get bridge hello time in seconds. + + Example: + + >>> from vyos.ifconfig import Interface + >>> BridgeIf('br0').hello_time + '2' + """ + + time = 0 + with open('/sys/class/net/{0}/bridge/hello_time'.format(self._ifname), 'r') as f: + time = int(f.read().rstrip('\n')) + + # kernel representation is in centiseconds - convert to seconds + return time/100 + + + @hello_time.setter + def hello_time(self, time=None): + """ + Set bridge hello time in seconds. + + Example: + + >>> from vyos.ifconfig import Interface + >>> BridgeIf('br0').hello_time = 2 + """ + + if not time: + raise ValueError() + + # kernel representation is in centiseconds - convert from seconds to centiseconds + time = int(time) * 100 + + with open('/sys/class/net/{0}/bridge/hello_time'.format(self._ifname), 'w') as f: + f.write(str(time)) + + @property + def max_age(self): + """ + Get bridge max max message age in seconds. + + Example: + + >>> from vyos.ifconfig import Interface + >>> BridgeIf('br0').max_age + '20' + """ + + time = 0 + with open('/sys/class/net/{0}/bridge/max_age'.format(self._ifname), 'r') as f: + time = int(f.read().rstrip('\n')) + + # kernel representation is in centiseconds - convert to seconds + return time/100 + + + @max_age.setter + def max_age(self, time=None): + """ + Set bridge max message age in seconds. + + Example: + + >>> from vyos.ifconfig import Interface + >>> BridgeIf('br0').max_age = 30 + """ + + if not time: + raise ValueError() + + # kernel representation is in centiseconds - convert from seconds to centiseconds + time = int(time) * 100 + + with open('/sys/class/net/{0}/bridge/max_age'.format(self._ifname), 'w') as f: + f.write(str(time)) + + @property + def priority(self): + """ + Get bridge max aging time in seconds. + + Example: + + >>> from vyos.ifconfig import Interface + >>> BridgeIf('br0').priority + '32768' + """ + + priority = 0 + with open('/sys/class/net/{0}/bridge/priority'.format(self._ifname), 'r') as f: + priority = int(f.read().rstrip('\n')) + + # kernel representation is in centiseconds - convert to seconds + return priority + + + @priority.setter + def priority(self, priority=None): + """ + Set bridge max aging time in seconds. + + Example: + + >>> from vyos.ifconfig import Interface + >>> BridgeIf('br0').priority = 8192 + """ + + if not priority: + raise ValueError() + + with open('/sys/class/net/{0}/bridge/priority'.format(self._ifname), 'w') as f: + f.write(str(priority)) + + + @property + def stp_state(self): + """ + Get bridge STP state + + Example: + + >>> from vyos.ifconfig import Interface + >>> BridgeIf('br0').stp_state + '0' + """ + + state = 0 + with open('/sys/class/net/{0}/bridge/stp_state'.format(self._ifname), 'r') as f: + state = int(f.read().rstrip('\n')) + + return state + + + @stp_state.setter + def stp_state(self, state=None): + """ + Set bridge STP state. + 0 -> STP disabled, 1 -> STP enabled + + Example: + + >>> from vyos.ifconfig import Interface + >>> BridgeIf('br0').stp_state = 1 + """ + + if int(state) >= 0 and int(state) <= 1: + with open('/sys/class/net/{0}/bridge/stp_state'.format(self._ifname), 'w') as f: + f.write(str(state)) else: - pid = open(pidfile, 'r').read() - print( - "dhclientv6 running on {0} with pid {1}".format(self._ifname, pid)) - return True + raise ValueError() + + + @property + def multicast_querier(self): + """ + Get bridge multicast querier membership state. + + Example: + + >>> from vyos.ifconfig import Interface + >>> BridgeIf('br0').multicast_querier + '0' + """ + + enable = 0 + with open('/sys/class/net/{0}/bridge/multicast_querier'.format(self._ifname), 'r') as f: + enable = int(f.read().rstrip('\n')) + + return enable + + + @multicast_querier.setter + def multicast_querier(self, enable=None): + """ + Sets whether the bridge actively runs a multicast querier or not. When a + bridge receives a 'multicast host membership' query from another network + host, that host is tracked based on the time that the query was received + plus the multicast query interval time. + + Use enable=1 to enable or enable=0 to disable + + Example: + + >>> from vyos.ifconfig import Interface + >>> BridgeIf('br0').multicast_querier = 1 + """ + + if int(enable) >= 0 and int(enable) <= 1: + with open('/sys/class/net/{0}/bridge/multicast_querier'.format(self._ifname), 'w') as f: + f.write(str(enable)) + else: + raise ValueError() + + + def add_port(self, interface=None): + """ + Add bridge member port + + Example: + + >>> from vyos.ifconfig import Interface + >>> BridgeIf('br0').add_port('eth0') + >>> BridgeIf('br0').add_port('eth1') + """ + + if not interface: + raise ValueError('No interface address specified') + + cmd = 'ip link set dev "{}" master "{}"'.format(interface, self._ifname) + self._cmd(cmd) + + + def del_port(self, interface=None): + """ + Add bridge member port + + Example: + + >>> from vyos.ifconfig import Interface + >>> BridgeIf('br0').del_port('eth1') + """ + + if not interface: + raise ValueError('No interface address specified') + + cmd = 'ip link set dev "{}" nomaster'.format(interface) + self._cmd(cmd) + + + def set_cost(self, interface=None, cost=None): + """ + Set interface path cost, only relevant for STP enabled interfaces + + Example: + + >>> from vyos.ifconfig import Interface + >>> Interface('eth0').path_cost(4) + """ + + if not interface: + raise ValueError('interface not specified') + + if not cost: + raise ValueError('cost not specified') + + with open('/sys/class/net/{}/brif/{}/path_cost'.format(self._ifname, interface), 'w') as f: + f.write(str(cost)) + + + def set_priority(self, interface=None, priority=None): + """ + Set interface path priority, only relevant for STP enabled interfaces + + Example: + + >>> from vyos.ifconfig import Interface + >>> Interface('eth0').priority(4) + """ + + if not interface: + raise ValueError('interface not specified') + if not priority: + raise ValueError('priority not specified') -# TODO: dhcpv6-pd via dhclient + with open('/sys/class/net/{}/brif/{}/priority'.format(self._ifname, interface), 'w') as f: + f.write(str(priority)) |