diff options
Diffstat (limited to 'src')
| -rwxr-xr-x | src/conf_mode/interfaces-pseudo-ethernet.py | 11 | ||||
| -rwxr-xr-x | src/conf_mode/system-options.py | 75 | ||||
| -rwxr-xr-x | src/op_mode/flow_accounting_op.py | 54 | 
3 files changed, 82 insertions, 58 deletions
| diff --git a/src/conf_mode/interfaces-pseudo-ethernet.py b/src/conf_mode/interfaces-pseudo-ethernet.py index 70710e97c..fb8237bee 100755 --- a/src/conf_mode/interfaces-pseudo-ethernet.py +++ b/src/conf_mode/interfaces-pseudo-ethernet.py @@ -36,7 +36,7 @@ default_config_data = {      'ip_arp_cache_tmo': 30,      'ip_proxy_arp_pvlan': 0,      'source_interface': '', -    'source_interface_changed': False, +    'recreating_required': False,      'mode': 'private',      'vif_s': {},      'vif_s_remove': [], @@ -79,11 +79,14 @@ def get_config():          peth['source_interface'] = conf.return_value(['source-interface'])          tmp = conf.return_effective_value(['source-interface'])          if tmp != peth['source_interface']: -            peth['source_interface_changed'] = True +            peth['recreating_required'] = True      # MACvlan mode      if conf.exists(['mode']):          peth['mode'] = conf.return_value(['mode']) +        tmp = conf.return_effective_value(['mode']) +        if tmp != peth['mode']: +            peth['recreating_required'] = True      add_to_dict(conf, disabled, peth, 'vif', 'vif')      add_to_dict(conf, disabled, peth, 'vif-s', 'vif_s') @@ -139,10 +142,10 @@ def apply(peth):          return None      # Check if MACVLAN interface already exists. Parameters like the underlaying -    # source-interface device can not be changed on the fly and the interface +    # source-interface device or mode can not be changed on the fly and the interface      # needs to be recreated from the bottom.      if peth['intf'] in interfaces(): -        if peth['source_interface_changed']: +        if peth['recreating_required']:              MACVLANIf(peth['intf']).remove()      # MACVLAN interface needs to be created on-block instead of passing a ton diff --git a/src/conf_mode/system-options.py b/src/conf_mode/system-options.py index 8de3b6fa2..d7c5c0443 100755 --- a/src/conf_mode/system-options.py +++ b/src/conf_mode/system-options.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2019 VyOS maintainers and contributors +# Copyright (C) 2019-2020 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 @@ -16,67 +16,62 @@  import os +from netifaces import interfaces  from sys import exit -from copy import deepcopy +  from vyos.config import Config +from vyos.template import render +from vyos.util import call  from vyos import ConfigError -from vyos.util import run -  from vyos import airbag  airbag.enable() -systemd_ctrl_alt_del = '/lib/systemd/system/ctrl-alt-del.target' - -default_config_data = { -    'beep_if_fully_booted': False, -    'ctrl_alt_del': 'ignore', -    'reboot_on_panic': True -} +config_file = r'/etc/curlrc' +systemd_action_file = '/lib/systemd/system/ctrl-alt-del.target'  def get_config(): -    opt = deepcopy(default_config_data)      conf = Config() -    conf.set_level('system options') -    if conf.exists(''): -        if conf.exists('ctrl-alt-del-action'): -            opt['ctrl_alt_del'] = conf.return_value('ctrl-alt-del-action') +    base = ['system', 'options'] +    options = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) +    return options -        opt['beep_if_fully_booted'] = conf.exists('beep-if-fully-booted') -        opt['reboot_on_panic'] = conf.exists('reboot-on-panic') +def verify(options): +    if 'http_client' in options.keys(): +        config = options['http_client'] +        if 'source_interface' in config.keys(): +            if not config['source_interface'] in interfaces(): +                raise ConfigError(f'Source interface {source_interface} does not ' +                                  f'exist'.format(**config)) -    return opt +        if {'source_address', 'source_interface'} <= set(config): +            raise ConfigError('Can not define both HTTP source-interface and source-address') -def verify(opt): -    pass +    return None -def generate(opt): -    pass +def generate(options): +    render(config_file, 'system/curlrc.tmpl', options, trim_blocks=True) +    return None -def apply(opt): +def apply(options):      # Beep action -    if opt['beep_if_fully_booted']: -        run('systemctl enable vyos-beep.service') +    if 'beep_if_fully_booted' in options.keys(): +        call('systemctl enable vyos-beep.service')      else: -        run('systemctl disable vyos-beep.service') +        call('systemctl disable vyos-beep.service')      # Ctrl-Alt-Delete action -    if opt['ctrl_alt_del'] == 'ignore': -        if os.path.exists(systemd_ctrl_alt_del): -            os.unlink('/lib/systemd/system/ctrl-alt-del.target') - -    elif opt['ctrl_alt_del'] == 'reboot': -        if os.path.exists(systemd_ctrl_alt_del): -            os.unlink(systemd_ctrl_alt_del) -        os.symlink('/lib/systemd/system/reboot.target', systemd_ctrl_alt_del) +    if os.path.exists(systemd_action_file): +        os.unlink(systemd_action_file) -    elif opt['ctrl_alt_del'] == 'poweroff': -        if os.path.exists(systemd_ctrl_alt_del): -            os.unlink(systemd_ctrl_alt_del) -        os.symlink('/lib/systemd/system/poweroff.target', systemd_ctrl_alt_del) +    if 'ctrl_alt_del_action' in options.keys(): +        if options['ctrl_alt_del_action'] == 'reboot': +            os.symlink('/lib/systemd/system/reboot.target', systemd_action_file) +        elif options['ctrl_alt_del_action'] == 'poweroff': +            os.symlink('/lib/systemd/system/poweroff.target', systemd_action_file)      # Reboot system on kernel panic      with open('/proc/sys/kernel/panic', 'w') as f: -        if opt['reboot_on_panic']: +        if 'reboot_on_panic' in options.keys():              f.write('60')          else:              f.write('0') diff --git a/src/op_mode/flow_accounting_op.py b/src/op_mode/flow_accounting_op.py index bf8c39fd6..9d0417cd4 100755 --- a/src/op_mode/flow_accounting_op.py +++ b/src/op_mode/flow_accounting_op.py @@ -21,8 +21,9 @@ import re  import ipaddress  import os.path  from tabulate import tabulate - +from json import loads  from vyos.util import cmd, run +from vyos.logger import syslog  # some default values  uacctd_pidfile = '/var/run/uacctd.pid' @@ -32,28 +33,28 @@ uacctd_pipefile = '/tmp/uacctd.pipe'  # check if ports argument have correct format  def _is_ports(ports):      # define regex for checking -    regex_filter = re.compile('^(\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$|^(\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])-(\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$|^((\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]),)+(\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$') +    regex_filter = re.compile(r'^(\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$|^(\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])-(\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$|^((\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]),)+(\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$')      if not regex_filter.search(ports):          raise argparse.ArgumentTypeError("Invalid ports: {}".format(ports))      # check which type nitation is used: single port, ports list, ports range      # single port -    regex_filter = re.compile('^(\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$') +    regex_filter = re.compile(r'^(\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$')      if regex_filter.search(ports): -        filter_ports = { 'type': 'single', 'value': int(ports) } +        filter_ports = {'type': 'single', 'value': int(ports)}      # ports list -    regex_filter = re.compile('^((\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]),)+(\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])') +    regex_filter = re.compile(r'^((\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]),)+(\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])')      if regex_filter.search(ports): -        filter_ports = { 'type': 'list', 'value': list(map(int, ports.split(','))) } +        filter_ports = {'type': 'list', 'value': list(map(int, ports.split(',')))}      # ports range -    regex_filter = re.compile('^(?P<first>\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])-(?P<second>\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$') +    regex_filter = re.compile(r'^(?P<first>\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])-(?P<second>\d|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$')      if regex_filter.search(ports):          # check if second number is greater than the first          if int(regex_filter.search(ports).group('first')) >= int(regex_filter.search(ports).group('second')):              raise argparse.ArgumentTypeError("Invalid ports: {}".format(ports)) -        filter_ports = { 'type': 'range', 'value': range(int(regex_filter.search(ports).group('first')), int(regex_filter.search(ports).group('second'))) } +        filter_ports = {'type': 'range', 'value': range(int(regex_filter.search(ports).group('first')), int(regex_filter.search(ports).group('second')))}      # if all above failed      if not filter_ports: @@ -61,6 +62,7 @@ def _is_ports(ports):      else:          return filter_ports +  # check if host argument have correct format  def _is_host(host):      # define regex for checking @@ -68,11 +70,13 @@ def _is_host(host):          raise argparse.ArgumentTypeError("Invalid host: {}".format(host))      return host +  # check if flow-accounting running  def _uacctd_running():      command = 'systemctl status uacctd.service > /dev/null'      return run(command) == 0 +  # get list of interfaces  def _get_ifaces_dict():      # run command to get ifaces list @@ -83,7 +87,7 @@ def _get_ifaces_dict():      # make a dictionary with interfaces and indexes      ifaces_dict = {} -    regex_filter = re.compile('^(?P<iface_index>\d+):\ (?P<iface_name>[\w\d\.]+)[:@].*$') +    regex_filter = re.compile(r'^(?P<iface_index>\d+):\ (?P<iface_name>[\w\d\.]+)[:@].*$')      for iface_line in ifaces_out:          if regex_filter.search(iface_line):              ifaces_dict[int(regex_filter.search(iface_line).group('iface_index'))] = regex_filter.search(iface_line).group('iface_name') @@ -91,11 +95,12 @@ def _get_ifaces_dict():      # return dictioanry      return ifaces_dict +  # get list of flows  def _get_flows_list():      # run command to get flows list      out = cmd(f'/usr/bin/pmacct -s -O json -T flows -p {uacctd_pipefile}', -                message='Failed to get flows list') +              message='Failed to get flows list')      # read output      flows_out = out.splitlines() @@ -103,11 +108,15 @@ def _get_flows_list():      # make a list with flows      flows_list = []      for flow_line in flows_out: -        flows_list.append(eval(flow_line)) +        try: +            flows_list.append(loads(flow_line)) +        except Exception as err: +            syslog.error('Unable to read flow info: {}'.format(err))      # return list of flows      return flows_list +  # filter and format flows  def _flows_filter(flows, ifaces):      # predefine filtered flows list @@ -149,14 +158,29 @@ def _flows_filter(flows, ifaces):      # return filtered flows      return flows_filtered +  # print flow table  def _flows_table_print(flows): -    #define headers and body -    table_headers = [ 'IN_IFACE', 'SRC_MAC', 'DST_MAC', 'SRC_IP', 'DST_IP', 'SRC_PORT', 'DST_PORT', 'PROTOCOL', 'TOS', 'PACKETS', 'FLOWS', 'BYTES' ] +    # define headers and body +    table_headers = ['IN_IFACE', 'SRC_MAC', 'DST_MAC', 'SRC_IP', 'DST_IP', 'SRC_PORT', 'DST_PORT', 'PROTOCOL', 'TOS', 'PACKETS', 'FLOWS', 'BYTES']      table_body = []      # convert flows to list      for flow in flows: -        table_body.append([flow['iface_in_name'], flow['mac_src'], flow['mac_dst'], flow['ip_src'], flow['ip_dst'], flow['port_src'], flow['port_dst'], flow['ip_proto'], flow['tos'], flow['packets'], flow['flows'], flow['bytes'] ]) +        table_line = [ +            flow.get('iface_in_name'), +            flow.get('mac_src'), +            flow.get('mac_dst'), +            flow.get('ip_src'), +            flow.get('ip_dst'), +            flow.get('port_src'), +            flow.get('port_dst'), +            flow.get('ip_proto'), +            flow.get('tos'), +            flow.get('packets'), +            flow.get('flows'), +            flow.get('bytes') +        ] +        table_body.append(table_line)      # configure and fill table      table = tabulate(table_body, table_headers, tablefmt="simple") @@ -168,12 +192,14 @@ def _flows_table_print(flows):      except KeyboardInterrupt:          sys.exit(0) +  # check if in-memory table is active  def _check_imt():      if not os.path.exists(uacctd_pipefile):          print("In-memory table is not available")          sys.exit(1) +  # define program arguments  cmd_args_parser = argparse.ArgumentParser(description='show flow-accounting')  cmd_args_parser.add_argument('--action', choices=['show', 'clear', 'restart'], required=True, help='command to flow-accounting daemon') | 
