summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debian/control1
-rw-r--r--interface-definitions/mdns-repeater.xml8
-rw-r--r--op-mode-definitions/traffic-dump.xml8
-rw-r--r--python/vyos/configtree.py11
-rwxr-xr-xsrc/conf_mode/mdns_repeater.py92
-rwxr-xr-xsrc/conf_mode/snmp.py15
-rwxr-xr-xsrc/migration-scripts/system/8-to-932
-rwxr-xr-xsrc/op_mode/show_dhcp.py104
8 files changed, 225 insertions, 46 deletions
diff --git a/debian/control b/debian/control
index 04b228737..b20690e75 100644
--- a/debian/control
+++ b/debian/control
@@ -21,6 +21,7 @@ Depends: python3,
python3-pystache,
python3-psutil,
python3-tabulate,
+ python3-six,
ipaddrcheck,
tcpdump,
bmon,
diff --git a/interface-definitions/mdns-repeater.xml b/interface-definitions/mdns-repeater.xml
index d74e203d6..a59321294 100644
--- a/interface-definitions/mdns-repeater.xml
+++ b/interface-definitions/mdns-repeater.xml
@@ -14,9 +14,15 @@
<priority>990</priority>
</properties>
<children>
+ <leafNode name="disable">
+ <properties>
+ <help>Disable mDNS repeater service</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<leafNode name="interface">
<properties>
- <help>Interface to repeat mdns advertisements to [REQUIRED]</help>
+ <help>Interface to repeat mDNS advertisements [REQUIRED]</help>
<completionHelp>
<script>${vyos_completion_dir}/list_interfaces.py</script>
</completionHelp>
diff --git a/op-mode-definitions/traffic-dump.xml b/op-mode-definitions/traffic-dump.xml
index a6810644e..00a809a7c 100644
--- a/op-mode-definitions/traffic-dump.xml
+++ b/op-mode-definitions/traffic-dump.xml
@@ -8,7 +8,7 @@
</properties>
<children>
<tagNode name="interface">
- <command>tcpdump -i $4</command>
+ <command>sudo tcpdump -i $4</command>
<properties>
<help>Monitor traffic dump from an interface</help>
<completionHelp>
@@ -17,19 +17,19 @@
</properties>
<children>
<tagNode name="filter">
- <command>tcpdump -n -i $4 $6</command>
+ <command>sudo tcpdump -n -i $4 $6</command>
<properties>
<help>Monitor traffic matching filter conditions</help>
</properties>
</tagNode>
<tagNode name="save">
- <command>tcpdump -n -i $4 -w $6</command>
+ <command>sudo tcpdump -n -i $4 -w $6</command>
<properties>
<help>Save traffic dump from an interface to a file</help>
</properties>
<children>
<tagNode name="filter">
- <command>tcpdump -n -i $4 -w $6 $8</command>
+ <command>sudo tcpdump -n -i $4 -w $6 $8</command>
<properties>
<help>Save a dump of traffic matching filter conditions to a file</help>
</properties>
diff --git a/python/vyos/configtree.py b/python/vyos/configtree.py
index 4b46a1fb3..ad8fcef1a 100644
--- a/python/vyos/configtree.py
+++ b/python/vyos/configtree.py
@@ -112,6 +112,10 @@ class ConfigTree(object):
self.__delete.argtypes = [c_void_p, c_char_p]
self.__delete.restype = c_int
+ self.__rename = self.__lib.rename_node
+ self.__rename.argtypes = [c_void_p, c_char_p, c_char_p]
+ self.__rename.restype = c_int
+
self.__set_replace_value = self.__lib.set_replace_value
self.__set_replace_value.argtypes = [c_void_p, c_char_p, c_char_p]
self.__set_replace_value.restype = c_int
@@ -193,6 +197,13 @@ class ConfigTree(object):
self.__delete_value(self.__config, path_str, value.encode())
+ def rename(self, path, newname):
+ check_path(path)
+ path_str = " ".join(map(str, path)).encode()
+ newname_str = newname.encode()
+
+ self.__rename(self.__config, path_str, newname_str)
+
def exists(self, path):
check_path(path)
path_str = " ".join(map(str, path)).encode()
diff --git a/src/conf_mode/mdns_repeater.py b/src/conf_mode/mdns_repeater.py
index 474a6a5cf..cef735c0d 100755
--- a/src/conf_mode/mdns_repeater.py
+++ b/src/conf_mode/mdns_repeater.py
@@ -18,7 +18,7 @@
import sys
import os
-
+import jinja2
import netifaces
from vyos.config import Config
@@ -26,60 +26,78 @@ from vyos import ConfigError
config_file = r'/etc/default/mdns-repeater'
-def get_config():
- interface_list = []
+config_tmpl = """
+### Autogenerated by mdns_repeater.py ###
+DAEMON_ARGS="{{ interfaces | join(' ') }}"
+"""
+
+default_config_data = {
+ 'disabled': False,
+ 'interfaces': []
+}
+def get_config():
+ mdns = default_config_data
conf = Config()
- conf.set_level('service mdns repeater')
- if not conf.exists(''):
- return interface_list
+ if not conf.exists('service mdns repeater'):
+ return None
+ else:
+ conf.set_level('service mdns repeater')
- if conf.exists('interface'):
- intfs_names = []
- intfs_names = conf.return_values('interface')
+ # Service can be disabled by user
+ if conf.exists('disable'):
+ mdns['disabled'] = True
+ return mdns
- for name in intfs_names:
- interface_list.append(name)
+ # Interface to repeat mDNS advertisements
+ if conf.exists('interface'):
+ mdns['interfaces'] = conf.return_values('interface')
- return interface_list
+ return mdns
def verify(mdns):
- # '0' interfaces are possible, think of service deletion. Only '1' is not supported!
- if len(mdns) == 1:
- raise ConfigError('At least 2 interfaces must be specified but %d given!' % len(mdns))
-
- # For mdns-repeater to work it is essential that the interfaces
- # have an IP address assigned
- for intf in mdns:
- try:
- netifaces.ifaddresses(intf)[netifaces.AF_INET]
- except KeyError as e:
- raise ConfigError('No IP address configured for interface "%s"!' % intf)
+ if mdns is None:
+ return None
+
+ if mdns['disabled']:
+ return None
+
+ # We need at least two interfaces to repeat mDNS advertisments
+ if len(mdns['interfaces']) < 2:
+ raise ConfigError('mDNS repeater requires at least 2 configured interfaces!')
+
+ # For mdns-repeater to work it is essential that the interfaces has
+ # an IPv4 address assigned
+ for interface in mdns['interfaces']:
+ if netifaces.AF_INET in netifaces.ifaddresses(interface).keys():
+ if len(netifaces.ifaddresses(interface)[netifaces.AF_INET]) < 1:
+ raise ConfigError('mDNS repeater requires an IPv6 address configured on interface %s!'.format(interface))
return None
def generate(mdns):
- config_header = '### Autogenerated by mdns_repeater.py ###\n'
- if len(mdns) > 0:
- config_args = 'DAEMON_ARGS="' + ' '.join(str(e) for e in mdns) + '"\n'
- else:
- config_args = 'DAEMON_ARGS=""\n'
+ if mdns is None:
+ return None
+
+ if mdns['disabled']:
+ print('Warning: mDNS repeater will be deactivated because it is disabled')
+ return None
- # write new configuration file
- f = open(config_file, 'w')
- f.write(config_header)
- f.write(config_args)
- f.close()
+ tmpl = jinja2.Template(config_tmpl)
+ config_text = tmpl.render(mdns)
+ with open(config_file, 'w') as f:
+ f.write(config_text)
return None
def apply(mdns):
- if len(mdns) == 0:
- cmd = "sudo systemctl stop mdns-repeater"
+ if (mdns is None) or mdns['disabled']:
+ os.system('sudo systemctl stop mdns-repeater')
+ if os.path.exists(config_file):
+ os.unlink(config_file)
else:
- cmd = "sudo systemctl restart mdns-repeater"
+ os.system('sudo systemctl restart mdns-repeater')
- os.system(cmd)
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py
index a4e776d49..3eb2935be 100755
--- a/src/conf_mode/snmp.py
+++ b/src/conf_mode/snmp.py
@@ -21,7 +21,6 @@ import os
import shutil
import stat
import pwd
-import time
import jinja2
import random
@@ -771,9 +770,17 @@ def apply(snmp):
# start SNMP daemon
os.system("sudo systemctl restart snmpd.service")
- # the passwords are not available immediately so this is a workaround
- # and should be changed to polling
- time.sleep(2)
+ # Passwords are not available immediately in the configuration file,
+ # after daemon startup - we wait until they have been processed by
+ # snmpd, which we see when a magic line appears in this file.
+ snmpReady = False
+ while not snmpReady:
+ with open(config_file_user, 'r') as f:
+ for line in f:
+ # Search for our magic string inside the file
+ if '**** DO NOT EDIT THIS FILE ****' in line:
+ snmpReady = True
+ break
# Back in the Perl days the configuration was re-read and any
# plaintext password inside the configuration was replaced by
diff --git a/src/migration-scripts/system/8-to-9 b/src/migration-scripts/system/8-to-9
new file mode 100755
index 000000000..db3fefdea
--- /dev/null
+++ b/src/migration-scripts/system/8-to-9
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3
+
+# Deletes "system package" option as it is deprecated
+
+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)
+
+if not config.exists(['system', 'package']):
+ # Nothing to do
+ sys.exit(0)
+else:
+ # Delete the node with the old syntax
+ config.delete(['system', 'package'])
+
+ 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/op_mode/show_dhcp.py b/src/op_mode/show_dhcp.py
new file mode 100755
index 000000000..2084a9509
--- /dev/null
+++ b/src/op_mode/show_dhcp.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018 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 <http://www.gnu.org/licenses/>.
+#
+
+import argparse
+
+import tabulate
+
+import vyos.config
+
+from isc_dhcp_leases import Lease, IscDhcpLeases
+
+
+lease_file = "/config/dhcpd.leases"
+pool_key = "shared-networkname"
+
+def in_pool(lease, pool):
+ if pool_key in lease.sets:
+ if lease.sets[pool_key] == pool:
+ return True
+
+ return False
+
+def get_lease_data(lease):
+ data = {}
+
+ # End time may not be present in backup leases
+ try:
+ data["expires"] = lease.end.strftime("%Y/%m/%d %H:%M:%S")
+ except:
+ data["expires"] = ""
+
+ data["hardware_address"] = lease.ethernet
+ data["hostname"] = lease.hostname
+ data["ip"] = lease.ip
+
+ try:
+ data["pool"] = lease.sets[pool_key]
+ except:
+ data["pool"] = ""
+
+ return data
+
+def get_leases(leases, state=None, pool=None):
+ leases = IscDhcpLeases(lease_file).get()
+
+ if state is not None:
+ leases = list(filter(lambda x: x.binding_state == 'active', leases))
+
+ if pool is not None:
+ leases = list(filter(lambda x: in_pool(x, pool), leases))
+
+ return list(map(get_lease_data, leases))
+
+def show_leases(leases):
+ headers = ["IP address", "Hardware address", "Lease expiration", "Pool", "Client Name"]
+
+ lease_list = []
+ for l in leases:
+ lease_list.append([l["ip"], l["hardware_address"], l["expires"], l["pool"], l["hostname"]])
+
+ output = tabulate.tabulate(lease_list, headers)
+
+ print(output)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+
+ group = parser.add_mutually_exclusive_group()
+ group.add_argument("-l", "--leases", action="store_true", help="Show DHCP leases")
+ group.add_argument("-s", "--statistics", action="store_true", help="Show DHCP statistics")
+
+ parser.add_argument("-e", "--expired", action="store_true", help="Show expired leases")
+ parser.add_argument("-p", "--pool", type=str, action="store", help="Show lease for specific pool")
+
+ args = parser.parse_args()
+
+ if args.leases:
+ if args.expired:
+ if args.pool:
+ leases = get_leases(lease_file, state='free', pool=args.pool)
+ else:
+ leases = get_leases(lease_file, state='free')
+ else:
+ if args.pool:
+ leases = get_leases(lease_file, state='active', pool=args.pool)
+ else:
+ leases = get_leases(lease_file, state='active')
+
+ show_leases(leases)