dist/
MANIFEST
_build/
-doc/
+doc/_build
+doc/src.rst
+doc/src.cnfline.rst
+doc/src.cnfvar.rst
# PyDev
.project
--- /dev/null
+Overview Diagram of cnfvar package
+==================================
+
+::
+
+ frontend . CRUD layer . backend drivers . backend
+ | | |
+ | | | .-,( ),-.
+ o | | | .-( )-.
+ /|\ | .----------. | .---------->( varlink API )
+ / \ |/| CnfStore | | | | '-( ).-'
+ / '----------' | | | '-.( ).-'
+ /| | | .-------------. | |
+ .----------' | | | | varlink API | | |
+ | CnfList | | '---------------->| interface | | | _.-----._
+ '--.------. | | '-------------' | | .- -.
+ |.-----.\ | | | '->|-_ _-|
+ || Cnf | \| .----------------. | | | ~-----~ |
+ |'-----' \ | BinaryCnfStore | | | .->| cnfvar |
+ |.-----. |'-----------------' | | | `._backend_.'
+ || Cnf | | | | .------------. | | "-----"
+ |'-----' | | | | subprocess | | |
+ |.-----. | '-------------->| wrappers | | _______
+ '| Cnf | | | '------------' | |get_cnf|
+ '-----' | | | | |set_cnf|
+ | | '------------> |_______|
+ | | | /:::::::/
+ | | |
+ ' ' '
+
# General information about the project.
project = 'pyi2ncommon'
-copyright = '2016-2018, Intra2net AG <info@intra2net.com>'
+copyright = 'Intra2net AG <info@intra2net.com>'
author = 'Intra2net AG'
# The version info for the project you're documenting, acts as replacement for
# built documents.
#
# The short X.Y version.
-version = '0'
+version = '1.6'
# The full version, including alpha/beta/rc tags.
-release = '1'
+release = '7'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
# Example configuration for intersphinx: refer to the Python standard library.
-intersphinx_mapping = {'https://docs.python.org/3.3': None}
+intersphinx_mapping = {'https://docs.python.org/3': None}
# show doc of class AND of constructor
autoclass_content = 'class' # 'both' not needed if special-members is used
def skip_class_members(app, what, name, obj, skip, options):
exclude = name in ('__weakref__', '__dict__', '__module__', '__doc__') \
and what == 'class'
- if exclude:
- print('excluding {0}'.format(name))
+ #if exclude:
+ # print('excluding {0}'.format(name))
return skip or exclude
def setup(app):
license.rst
about_docu.rst
- template
+ cnfvar-api-ascii-art.rst
Modules
# This exception does not invalidate any other reasons why a work based
# on this file might be covered by the GNU General Public License.
#
-# Copyright (c) 2016-2018 Intra2net AG <info@intra2net.com>
+# Copyright (c) 2016-2022 Intra2net AG <info@intra2net.com>
# For packaging pyi2ncommon into an rpm, use make_dist.py
author='Intra2net AG',
author_email='info@intra2net.com',
url='http://www.intra2net.com',
- packages=['pyi2ncommon', 'pyi2ncommon.cnfline'],
+ packages=['pyi2ncommon', 'pyi2ncommon.cnfline', 'pyi2ncommon.cnfvar'],
package_dir={'pyi2ncommon': 'src'},
license_files=('COPYING.GPL', 'Linking-Exception.txt'),
license='GPLv2 + linking exception',
--- /dev/null
+# The software in this package is distributed under the GNU General
+# Public License version 2 (with a special exception described below).
+#
+# A copy of GNU General Public License (GPL) is included in this distribution,
+# in the file COPYING.GPL.
+#
+# As a special exception, if other files instantiate templates or use macros
+# or inline functions from this file, or you compile this file and link it
+# with other works to produce a work based on this file, this file
+# does not by itself cause the resulting work to be covered
+# by the GNU General Public License.
+#
+# However the source code for this file must still be made available
+# in accordance with section (3) of the GNU General Public License.
+#
+# This exception does not invalidate any other reasons why a work based
+# on this file might be covered by the GNU General Public License.
+#
+# Copyright (c) 2016-2022 Intra2net AG <info@intra2net.com>
+
+"""
+arnied_api: wrappers around the arnied varlink API.
+
+Featuring
+- Arnied: stateless class with methods as exposed in the varlink API.
+
+For documentation of Exceptions, methods and their arguments, refer to arnied
+source code (arnied/client/arnieclient.hxx). For compatibility, argument names
+are the same here as they are there (defeating python naming rules).
+
+See package `cnfvar` for a higher-level interface to this functionality.
+
+.. codeauthor:: Intra2net
+"""
+
+from contextlib import contextmanager
+from dataclasses import dataclass
+from types import SimpleNamespace
+from enum import Enum, auto
+import typing
+import json
+import sys
+
+
+#: socket where the varlink interface is exposed
+ARNIED_SOCKET = "/var/intranator/arnied-varlink.sock"
+
+
+# Interface types
+
+
+@dataclass
+class CnfVar:
+ name: str
+ instance: int
+ data: str
+ deleted: bool
+ children: typing.List['CnfVar']
+
+
+@dataclass
+class ChangeCnfVar:
+ chg_nr: int
+ name: str
+ instance: int
+ data: str
+ result_type: int
+ result_msg: str
+
+
+@dataclass
+class GetCnfQuery:
+ name: str
+ instance: typing.Optional[int] = None
+
+
+class ProgramStatus(Enum):
+ Running = auto()
+ Scheduled = auto()
+ NotScheduled = auto()
+
+
+# Method return types (not present in the interface)
+
+class IsQueueActiveRet(SimpleNamespace):
+ active: bool
+
+
+class IsScheduledOrRunningRet(SimpleNamespace):
+ status: ProgramStatus
+ timestamp: typing.Optional[int]
+
+
+class GetCnfRet(SimpleNamespace):
+ vars: typing.List[CnfVar]
+
+
+class SetCnfRet(SimpleNamespace):
+ change_numbers: typing.List[int]
+
+
+class SetCommitCnf(SimpleNamespace):
+ results: typing.List[ChangeCnfVar]
+
+
+class CommitCnfRet(SimpleNamespace):
+ results: typing.List[ChangeCnfVar]
+
+
+# Exceptions
+
+
+class InternalBridgeError(Exception):
+ def __init__(self):
+ super().__init__("An error occurred (InternalBridgeError)")
+
+
+class EmptyInputError(Exception):
+ def __init__(self):
+ super().__init__("An error occurred (EmptyInputError)")
+
+
+class BadCharError(Exception):
+ def __init__(self, results: str) -> None:
+ super().__init__(f"[BadCharError] Error in the arnied API (results={results})")
+ self.results = results
+
+
+class ChildError(Exception):
+ def __init__(self, results: str) -> None:
+ super().__init__(f"[ChildError] Error in the arnied API (results={results})")
+ self.results = results
+
+
+class CnfCommitError(Exception):
+ def __init__(self, results: typing.List[ChangeCnfVar]) -> None:
+ self.results = []
+ msgs = []
+ for r in results:
+ # the type does not seem to be converted correctly
+ if isinstance(r, dict):
+ r = ChangeCnfVar(**r)
+ self.results.append(r)
+ msgs.append(f"{r.name},{r.instance}: \"{r.data}\": {r.result_msg}")
+ super().__init__("Error committing cnfvars:\n" + "\n".join(msgs))
+
+
+class NotFoundError(Exception):
+ def __init__(self, results: str) -> None:
+ super().__init__(f"[NotFoundError] Error in the arnied API (results={results})")
+ self.results = results
+
+
+class SchedulerProgError(Exception):
+ def __init__(self):
+ super().__init__("An error occurred (SchedulerProgError)")
+
+
+class ShowerrVarDataErr(Exception):
+ def __init__(self, msg_id: int) -> None:
+ super().__init__(f"[ShowerrVarDataErr] Error in the arnied API (msg_id={msg_id})")
+ self.msg_id = msg_id
+
+
+class ShowerrVarMissing(Exception):
+ def __init__(self, params_count: int) -> None:
+ super().__init__(
+ f"[ShowerrVarMissing] Error in the arnied API (params_count={params_count})")
+ self.params_count = params_count
+
+
+class ProviderNotFound(Exception):
+ def __init__(self, provider_id: int) -> None:
+ super().__init__(
+ f"[ProviderNotFound] Could not find provider (provider_id={provider_id})")
+ self.provider_id = provider_id
+
+
+# Arnied varlink proxy
+
+
+class Arnied:
+ """
+ Expose methods of the arnied varlink interface in python.
+
+ As described in module doc, documentation of exceptions, methods, and
+ their arguments can be found in arnied source code.
+ """
+ VARLINK_ADDRESS = f"unix:{ARNIED_SOCKET}"
+
+ @classmethod
+ def _send_msg_wrapper(cls, send_msg):
+ """
+ Wrap the _send_message method of the client to always send parameters.
+
+ HACK: this is as hackish as it comes, but at the moment we do not
+ have another workaround. The underlying varlink package will only
+ send a dictionary with a "parameters" key when we pass parameters,
+ however methods with nullable arguments require this key, in which
+ case the API errors out. One example is the `GetCnf` method: if
+ we want to get *all* cnfvars we should not provide it with any args,
+ but since the varlink module will not pass a dictionary with a
+ "parameters" key, it will fail. This has been reported upstream:
+ https://github.com/varlink/python/issues/10#issuecomment-1067232162
+ """
+ def _send_msg(msg):
+ data = json.loads(msg.decode("utf8"))
+ if not data.get("method", "").startswith("io.arnied"):
+ return send_msg(msg)
+ if data.get("parameters", {}):
+ return send_msg(msg)
+ data["parameters"] = {}
+ return send_msg(json.dumps(data).encode("utf8"))
+ return _send_msg
+
+ @classmethod
+ @contextmanager
+ def new_connection(cls):
+ # lazy import to reduce the scope of this dependency
+ import varlink
+ client = varlink.Client.new_with_address(cls.VARLINK_ADDRESS)
+ conn = client.open("io.arnied", namespaced=True)
+ try:
+ sm = conn._send_message
+ setattr(conn, "_send_message", Arnied._send_msg_wrapper(sm))
+ yield conn
+ except varlink.VarlinkError as ex:
+ # not an exception from this module
+ if not ex.error().startswith("io.arnied"):
+ raise
+
+ # raise the corresponding exception
+ error_class_name = ex.error().split(".")[-1]
+ self_mod = sys.modules[__name__]
+ exception_class = self_mod.__dict__[error_class_name]
+ raise exception_class(**ex.parameters())
+ finally:
+ conn.close()
+ client.cleanup()
+
+ @classmethod
+ def is_queue_active(cls) -> IsQueueActiveRet:
+ with cls.new_connection() as conn:
+ return conn.IsQueueActive()
+
+ @classmethod
+ def is_scheduled_or_running(cls, prog: str) -> IsScheduledOrRunningRet:
+ with cls.new_connection() as conn:
+ return conn.IsScheduledOrRunning(prog)
+
+ @classmethod
+ def noop(cls) -> None:
+ with cls.new_connection() as conn:
+ return conn.NoOp()
+
+ @classmethod
+ def no_timeout(cls) -> None:
+ with cls.new_connection() as conn:
+ return conn.NoTimeout()
+
+ @classmethod
+ def get_cnf(cls, query: typing.Optional[GetCnfQuery]) -> GetCnfRet:
+ with cls.new_connection() as conn:
+ return conn.GetCnf(query)
+
+ @classmethod
+ def set_cnf(cls, vars: typing.List[CnfVar]) -> SetCnfRet:
+ with cls.new_connection() as conn:
+ return conn.SetCnf(vars)
+
+ @classmethod
+ def set_commit_cnf(cls, vars: typing.List[CnfVar], username: typing.Optional[str],
+ nogenerate: bool, fix_commit: bool) -> SetCommitCnf:
+ with cls.new_connection() as conn:
+ return conn.SetCommitCnf(vars, username, nogenerate, fix_commit)
+
+ @classmethod
+ def commit_cnf(cls, change_numbers: typing.List[int], username: typing.Optional[str],
+ nogenerate: bool, fix_commit: bool) -> CommitCnfRet:
+ with cls.new_connection() as conn:
+ return conn.CommitCnf(change_numbers, username, nogenerate, fix_commit)
+
+ @classmethod
+ def showerr_add(cls, msg_id: int, params: typing.List[str]) -> None:
+ with cls.new_connection() as conn:
+ return conn.ShowerrAdd(msg_id, params)
+
+ @classmethod
+ def now_online(cls, provider: int) -> None:
+ with cls.new_connection() as conn:
+ return conn.NowOnline(provider)
+
+ @classmethod
+ def now_offline(cls) -> None:
+ with cls.new_connection() as conn:
+ return conn.NowOffline()
+
+ @classmethod
+ def now_dialing(cls, provider: int) -> None:
+ with cls.new_connection() as conn:
+ return conn.NowDialing(provider)
+
+ @classmethod
+ def dial_taking_ages(cls) -> None:
+ with cls.new_connection() as conn:
+ return conn.DialTakingAges()
+
+ @classmethod
+ def barrier_executed(cls, barrier_nr: int) -> None:
+ with cls.new_connection() as conn:
+ return conn.BarrierExecuted(barrier_nr)
------------------------------------------------------
Guest utility to wrap arnied related functionality through python calls.
-Copyright: Intra2net AG
+DEPRECATED! Please do not extend this or add new uses of this module, use
+:py:mod:`pyi2ncommon.arnied_api` or :py:mod:`pyi2ncommon.cnfvar` instead!
+Copyright: Intra2net AG
There are three types of setting some cnfvar configuration:
log = logging.getLogger('pyi2ncommon.arnied_wrapper')
from .cnfline import build_cnfvar
-from . import cnfvar
+from . import cnfvar_old
from . import sysmisc
# the output of "get_cnf" (no json) which is latin1.
if isinstance(raw, bytes):
raw = raw.decode("utf-8")
- cnf = cnfvar.read_cnf_json(raw)
+ cnf = cnfvar_old.read_cnf_json(raw)
except TypeError as exn:
log.info("error \"%s\" parsing result of \"%s\"", exn, cmd_line)
return None
- except cnfvar.InvalidCNF as exn:
+ except cnfvar_old.InvalidCNF as exn:
log.info("error \"%s\" validating result of \"%s\"", exn, cmd_line)
return None
if data is not None:
- return cnfvar.get_vars(cnf, data=data)
+ return cnfvar_old.get_vars(cnf, data=data)
return cnf
fd = open(config_path, "wb")
try:
SET_CNF_METHODS = {
- "raw": cnfvar.write_cnf_raw,
- "json": cnfvar.write_cnf_json,
- "cnf": cnfvar.write_cnf
+ "raw": cnfvar_old.write_cnf_raw,
+ "json": cnfvar_old.write_cnf_json,
+ "cnf": cnfvar_old.write_cnf
}
SET_CNF_METHODS[kind](cnf, out=fd)
except KeyError:
--- /dev/null
+from .model import Cnf, CnfList
+from .binary import CnfBinary
+from .store import CnfStore, BinaryCnfStore, CommitException
+
+__all__ = ["Cnf", "CnfList", "CnfBinary", "CnfStore",
+ "BinaryCnfStore", "CommitException"]
--- /dev/null
+# The software in this package is distributed under the GNU General
+# Public License version 2 (with a special exception described below).
+#
+# A copy of GNU General Public License (GPL) is included in this distribution,
+# in the file COPYING.GPL.
+#
+# As a special exception, if other files instantiate templates or use macros
+# or inline functions from this file, or you compile this file and link it
+# with other works to produce a work based on this file, this file
+# does not by itself cause the resulting work to be covered
+# by the GNU General Public License.
+#
+# However the source code for this file must still be made available
+# in accordance with section (3) of the GNU General Public License.
+#
+# This exception does not invalidate any other reasons why a work based
+# on this file might be covered by the GNU General Public License.
+#
+# Copyright (c) 2016-2022 Intra2net AG <info@intra2net.com>
+
+"""
+binary: wrappers around binaries that work with the CNF store.
+
+Featuring:
+ - CnfBinary: stateless class that can be used to invoke the different binaries
+ in a Python-friendly way.
+
+.. note:: It is written as a class on purpose for it to be easily extended to
+ support invoking non-local binaries, but methods are all class methods since
+ the class is stateless.
+
+.. seealso:: Overview Diagram linked to from doc main page
+
+.. codeauthor:: Intra2net
+"""
+
+import subprocess
+import logging
+import shlex
+import html
+import re
+
+log = logging.getLogger("pyi2ncommon.cnfvar.binary")
+
+#: default get_cnf binary
+BIN_GET_CNF = "/usr/intranator/bin/get_cnf"
+#: default set_cnf binary
+BIN_SET_CNF = "/usr/intranator/bin/set_cnf"
+#: encoding used by the get_cnf and set_cnf binaries
+ENCODING = "latin1"
+
+
+class CnfBinary:
+ """Provide wrappers around the multiple binaries to handle the CNF store."""
+
+ @classmethod
+ def run_cmd(cls, cmd, cmd_input=None, ignore_errors=False, timeout=60, encoding=ENCODING):
+ """
+ Run commands on the local machine with input via stdin.
+
+ :param cmd: command to run
+ :type cmd: str or list
+ :param str cmd_input: input to give to the command
+ :param bool ignore_errors: whether to ignore the exit code or raise if not zero
+ :param int timeout: amount of seconds to wait for the command to complete
+ :param str encoding: encoding to use (pass latin1 when not working with JSON)
+ :returns: command result
+ :rtype: :py:class:`subprocess.CompletedProcess`
+ """
+ if isinstance(cmd, str):
+ cmd = shlex.split(cmd)
+
+ log.debug("Running binary cmd `%s` with input:\n%s",
+ ' '.join(cmd), cmd_input)
+ retval = subprocess.run(cmd, input=cmd_input, check=not ignore_errors,
+ capture_output=True, encoding=encoding, timeout=timeout)
+ log.debug("Command exited with \n"
+ "\treturncode=%d\n"
+ "\tstderr=%s\n"
+ "\tstdout=%s", retval.returncode, retval.stderr, retval.stdout)
+ return retval
+
+ @classmethod
+ def get_cnf(cls, name=None, instance=None, no_children=False,
+ as_json=False):
+ """
+ Wrapper around the `get_cnf` binary.
+
+ :param str name: optional name of the CNFs to get
+ :param instance: CNF instance
+ :type instance: str or int
+ :param bool no_children: whether to return child CNFs
+ :param bool as_json: whether to return a JSON-formatted string
+ :returns: output of the tool
+ :rtype: str
+
+ .. note:: being a wrapper, this function does not do anything extra
+ like checking if arnied is running or waiting for generate.
+ """
+ if name is None and instance is not None:
+ raise ValueError("cannot pass `instance` without a `name`")
+
+ if isinstance(instance, str):
+ instance = int(instance) # validate
+ elif instance is not None and not isinstance(instance, int):
+ raise TypeError(f"`instance` is of wrong type {type(instance)}")
+
+ cmd = f"{BIN_GET_CNF} {name or ''} {instance or ''}"
+
+ encoding = ENCODING
+ if no_children:
+ cmd += " --no-childs"
+ if as_json:
+ cmd += " --json"
+ encoding = "utf8"
+
+ # TODO: should error handling be improved so error messages
+ # from the binary are converted into specific exceptions types?
+ output = cls.run_cmd(cmd, encoding=encoding).stdout.strip()
+ # remove escape chars (such as '//')
+ output = output.replace('\\"', '"')
+ return output
+
+ @classmethod
+ def set_cnf(cls, input_str=None, input_file=None, delete=False,
+ delete_file=False, as_json=False, fix_problems=False, **kwargs):
+ """
+ Wrapper around the `set_cnf` binary.
+
+ :param str input_str: string to send as input to the binary
+ :param str input_file: path to a file to pass to the binary
+ :param bool delete: whether to delete the corresponding CNFs
+ :param bool delete_file: whether to delete the file passed as input
+ :param bool as_json: whether to interpret input as JSON
+ :param bool fix_problems: whether to automatically fix errors in the vars
+ :param kwargs: extra arguments to pass to the binary - underscores are
+ replaced by dash, e.g. set_cnf(..., enable_queue=True)
+ becomes `/usr/intranator/bin/set_cnf --enable-queue`
+ :raises: :py:class:`SetCnfException` in case the binary errors out
+
+ .. note:: being a wrapper, this function does not do anything extra
+ like checking if arnied is running or waiting for generate.
+ """
+ if input_str is None and input_file is None:
+ raise ValueError("Need to pass either a string or a file")
+
+ if delete_file is True and input_file is None:
+ raise ValueError("Cannot delete an unspecified file")
+
+ cmd = f"{BIN_SET_CNF}"
+ encoding = ENCODING
+ if as_json:
+ cmd += " --json"
+ encoding = "utf8"
+ if delete:
+ cmd += " -x"
+ if delete_file:
+ cmd += " --delete-file"
+ if fix_problems:
+ cmd += " --fix-problems"
+
+ for k, v in kwargs.items():
+ if v is True:
+ k = "-".join(k.split("_"))
+ cmd += f" --{k}"
+
+ if input_file:
+ cmd += f" {input_file}"
+
+ try:
+ cls.run_cmd(cmd, cmd_input=input_str, encoding=encoding)
+ except subprocess.CalledProcessError as ex:
+ # clean up the error message
+ ex.stderr = cls._clean_set_cnf_error(ex.stderr)
+ raise
+
+ @classmethod
+ def _clean_set_cnf_error(cls, err):
+ """
+ Turn the error from the set_cnf binary into a more readable message.
+
+ :param str err: error message from the binary
+ :returns: the clean error message
+ :rtype: str
+
+ Keep only offending lines and strip out HTML tags.
+ """
+ def get_error_lines(output_lines):
+ for cnfline in output_lines:
+ buffer = ""
+ quoted = False
+ parts = []
+ for c in cnfline:
+ last_chr = buffer[-1] if buffer else None
+ # this is a literal (unescaped) quote
+ if c == "\"" and last_chr != "\\":
+ quoted = not quoted
+
+ if c == " " and not quoted:
+ parts.append(buffer)
+ buffer = ""
+ else:
+ buffer += c
+ parts.append(buffer)
+
+ if parts[-2] == "0":
+ # no errors, ignore
+ continue
+
+ lineno, name, instance, parent, value, exit_code, error = parts
+
+ # the binary outputs HTML strings
+ error = html.unescape(error)
+ # replace line breaks for readability
+ error = re.sub(r"(<br\s*>)+", " ", error)
+ # clean up HTML tags
+ error = re.sub(r"<[^<]+?>", "", error)
+
+ if parent == "-1":
+ message = f"`` {lineno} {name},{instance}: {value} ``"
+ else:
+ message = f"`` {lineno} ({parent}) {name},{instance}: {value} ``"
+ yield f"Error in {message}: {error} (code={exit_code})"
+
+ lines = err.splitlines()
+ if len(lines) == 0:
+ return err
+
+ if "fatal errors in changes" not in lines[0]:
+ return err
+
+ errors = list(get_error_lines(lines[1:]))
+ return "\n".join(errors)
--- /dev/null
+# The software in this package is distributed under the GNU General
+# Public License version 2 (with a special exception described below).
+#
+# A copy of GNU General Public License (GPL) is included in this distribution,
+# in the file COPYING.GPL.
+#
+# As a special exception, if other files instantiate templates or use macros
+# or inline functions from this file, or you compile this file and link it
+# with other works to produce a work based on this file, this file
+# does not by itself cause the resulting work to be covered
+# by the GNU General Public License.
+#
+# However the source code for this file must still be made available
+# in accordance with section (3) of the GNU General Public License.
+#
+# This exception does not invalidate any other reasons why a work based
+# on this file might be covered by the GNU General Public License.
+#
+# Copyright (c) 2016-2022 Intra2net AG <info@intra2net.com>
+
+"""
+model: Cnf classes, collection of Cnf classes and multiple filtering methods.
+
+Featuring:
+ - Cnf: class representing a CNF variable
+ - CnfList: a collection of `Cnf` instances
+
+The classes above inherit from their base types with added mixins which
+extend them with extra functionality.
+
+.. seealso:: Overview Diagram linked to from doc main page
+
+.. codeauthor:: Intra2net
+"""
+
+import json
+
+from .. import cnfvar_old, arnied_api
+
+#: value used to detect unspecified arguments
+DEFAULT = object()
+#: encoding used by the get_cnf and set_cnf binaries
+ENCODING = "latin1"
+
+
+###############################################################################
+# HELPERS
+###############################################################################
+
+
+class CnfName(str):
+ """
+ Custom string where comparisons are case-insensitive.
+
+ With this class we do not have to worry about case when comparing against
+ the name of cnfvars when filtering. The cnfvar backend is already case-
+ insensitive anyway.
+ """
+
+ def __eq__(self, other):
+ if not isinstance(other, str):
+ return False
+ return self.lower() == other.lower()
+
+ def __contains__(self, name):
+ return name.lower() in self.lower()
+
+ def startswith(self, prefix, *args, **kwargs):
+ return self.lower().startswith(prefix.lower(), *args, **kwargs)
+
+ def endswith(self, prefix, *args, **kwargs):
+ return self.lower().endswith(prefix.lower(), *args, **kwargs)
+
+
+###############################################################################
+# BASE API
+###############################################################################
+
+
+class BaseCnfList(list):
+ """Base class representing a CNF list with minimal functionality."""
+
+ def __init__(self, cnf_iter=None, renumber=False):
+ """
+ Class constructor.
+
+ :param cnf_iter: iterator producing CNF elements or arguments for the
+ constructor of the :py:class:`Cnf` class
+ :type: :py:class:`collections.abc.Iterator` producing elements of type
+ :py:class:`Cnf`
+ :param bool renumber: whether to fix up the number/ids of the CNFs
+
+ Example::
+ cnf = Cnf("my_cnf", "value")
+ cnf_list = CnfList([
+ cnf,
+ ("other_cnf", "other value"),
+ ("user", "john", instance=3)
+ ])
+ """
+ # Map the values of the iterator to support constructing this list
+ # from Cnf instances or arguments to the Cnf constructor
+ if cnf_iter is not None:
+ iter_ = map(lambda c: c if isinstance(c, Cnf) else Cnf(*c), cnf_iter)
+ else:
+ iter_ = []
+ super().__init__(iter_)
+ self._renumber_counter = None # initialized and used in renumber
+ if renumber:
+ self.renumber()
+
+ def renumber(self):
+ """Fix line numbers of CNF variables from this list."""
+ # NOTE: we don't keep track of operations that change the list as this
+ # would require us to reimplement most of the methods. At least for now
+ # this method should be called again when serializing.
+ self._renumber_counter = 0
+
+ def renumber_fn(cnf):
+ self._renumber_counter += 1
+ cnf.lineno = self._renumber_counter
+
+ self.for_each_all(renumber_fn)
+
+ def where(self, where_filter):
+ """
+ Filter CNFs matching a given predicate.
+
+ :param where_filter: predicate to apply against CNFs
+ :type where_filter: function accepting a CNF and returning a boolean
+ :returns: an instance of this class with filtered members
+ :rtype: :py:class:`CnfList`
+ """
+ return CnfList(c for c in self if where_filter(c))
+
+ def where_child(self, where_filter):
+ """
+ Filter CNFs with children matching a given predicate.
+
+ :param where_filter: predicate to apply against children
+ :type where_filter: function accepting a CNF and returning a boolean
+ :returns: an instance of this class with filtered members
+ :rtype: :py:class:`CnfList`
+ """
+ def upper_filter(cnf):
+ return any(ch for ch in cnf.children if where_filter(ch))
+ return self.where(upper_filter)
+
+ def remove_where(self, where_filter):
+ """
+ Remove all CNFs from this list matching the given predicate.
+
+ :param where_filter: predicate to apply against children
+ :type where_filter: function accepting a CNF and returning a boolean
+ :returns: a list of the removed CNF variables
+ :rtype: [:py:class:`Cnf`]
+ """
+ r = []
+ # iterate by index for speed and in reverse to keep indexes valid
+ for i in range(len(self) - 1, -1, -1):
+ cnf = self[i]
+ if where_filter(cnf):
+ del self[i]
+ r.append(cnf)
+ return r
+
+ def for_each(self, fn):
+ """
+ Apply a function to each element of this list.
+
+ :param fn: function to apply to the elements
+ :type fn: function accepting a CNF (result value is ignored)
+ :returns: this same instance
+ :rtype: :py:class:`CnfList`
+
+ .. note:: this is mostly the same as the built-in map() function,
+ except that it changes the list in place.
+ """
+ for c in self:
+ try:
+ fn(c)
+ except StopIteration:
+ # support breaking
+ break
+ return self
+
+ def for_each_child(self, fn):
+ """
+ Apply a function to each child of the elements of this list.
+
+ :param fn: function to apply to the elements
+ :type fn: function accepting a CNF (result value is ignored)
+ :returns: this same instance
+ :rtype: :py:class:`CnfList`
+
+ .. note:: if a CNF does not have children, it is ignored
+ """
+ for c in self:
+ children = c.children or CnfList()
+ for ch in children:
+ try:
+ fn(ch)
+ except StopIteration:
+ # support breaking
+ break
+ # apply recursively, too
+ children.for_each_child(fn)
+ return self
+
+ def for_each_all(self, fn):
+ """
+ Apply a function to every CNF of this list, parent or child.
+
+ :param fn: function to apply to the elements
+ :type fn: function accepting a CNF (result value is ignored)
+ :returns: this same instance
+ :rtype: :py:class:`CnfList`
+ """
+ for c in self:
+ try:
+ fn(c)
+ except StopIteration:
+ # support breaking
+ break
+ children = c.children or CnfList()
+ children.for_each_all(fn)
+ return self
+
+ def __str__(self):
+ """
+ Get a string representation of this instance.
+
+ :returns: a string in the cnfvar format
+ :rtype: str
+ """
+ return "\n".join((str(c) for c in self))
+
+ def __add__(self, other):
+ return CnfList(super().__add__(other))
+
+
+class BaseCnf:
+ """Base class representing a CNF variable with minimal functionality."""
+
+ _PARENT_TEMPLATE = "{lineno} {name},{instance}: \"{value}\""
+ _CHILD_TEMPLATE = "{lineno} {indent}({parent}) {name},{instance}: \"{value}\""
+ _NEST_INDENT = " "
+
+ def __init__(self, name, value, instance=-1, parent=None,
+ lineno=None, comment=None):
+ """
+ Create this instance.
+
+ :param str name: name of the cnfvar (case does not matter)
+ :param str value: value for this cnfvar (will be converted to string
+ if it is not of this type)
+ :param int instance: instance of this cnfvar
+ :param parent: a parent Cnf instance
+ :type parent: :py:class:`BaseCnf`
+ :param int lineno: line number
+ :param str comment: cnfvar comment
+ """
+ self.name = CnfName(name)
+ self.value = value
+ self.instance = int(instance)
+ self.parent = parent
+ self.lineno = int(lineno or 0)
+ self.comment = comment
+ self.__children = CnfList()
+
+ # Use getters and setters to keep internal consistency and fail-fast
+ # preventing invalid data from being sent to the cnfvar backend.
+
+ def _get_name(self):
+ return self.__name
+
+ def _set_name(self, value):
+ # convert Python strings passed as name to our custom string
+ self.__name = CnfName(value)
+ name = property(_get_name, _set_name)
+
+ def _get_instance(self):
+ return self.__instance
+
+ def _set_instance(self, value):
+ # fail-fast and make sure instance is a valid integer
+ self.__instance = int(value)
+ instance = property(_get_instance, _set_instance)
+
+ def _get_lineno(self):
+ return self.__lineno
+
+ def _set_lineno(self, value):
+ # fail-fast and make sure lineno is a valid integer
+ self.__lineno = int(value)
+ lineno = property(_get_lineno, _set_lineno)
+
+ def _get_children(self):
+ return self.__children
+ # No setter to sure that the children property will not
+ # be replaced by something other than a `CnfList`
+ children = property(_get_children)
+
+ def _get_value(self):
+ return self.__value
+
+ def _set_value(self, value):
+ # Make sure the value is always stored as a string
+ # (no other types make sense to the cnfvar backend)
+ self.__value = str(value)
+ value = property(_get_value, _set_value)
+
+ def add_child(self, *args, **kwargs):
+ """
+ Add a child CNF variable.
+
+ Arguments can either be a single instance of the :py:class:`Cnf`
+ class or a list of arguments to be passed to the constructor of
+ that class.
+
+ :returns: the instance that was created
+ :rtype: :py:class:`Cnf`
+
+ Example::
+
+ cnf = Cnf("my_parent_cnf", "parent")
+ cnf2 = Cnf("my_child_cnf", "john")
+
+ # adding a child as a CNF instance
+ cnf.add_child(cnf2)
+
+ # adding a child passing arguments of the Cnf constructor
+ cnf.add_child("my_child_cnf", "jane", instance=2)
+ """
+ # support passing a Cnf instance
+ if len(args) == 1 and not kwargs:
+ cnf = args[0]
+ assert isinstance(cnf, Cnf), \
+ "With one argument, a Cnf instance is mandatory"
+ else:
+ cnf = Cnf(*args, **kwargs)
+
+ # store a reference to parent to easily access it
+ cnf.parent = self
+
+ # It seems the CNF backend (at least using set_cnf as opposed to the varlink
+ # API) only accepts instance with value of -1 for top-level variables, so
+ # just in case fix up instances when adding children with the default value.
+ if cnf.instance == -1:
+ cnf.instance = 0
+ for c in self.children:
+ if c.name == cnf.name:
+ cnf.instance += 1
+
+ self.children.append(cnf)
+ return cnf
+
+ def add_children(self, *children):
+ """
+ Add multiple child CNF variables.
+
+ Each argument must be either an instance of the :py:class:`Cnf` class
+ or a tuple/list to be expanded and passed to construct that instance.
+
+ :returns: a list of the instances that were created
+ :rtype: :py:class:`CnfList`
+
+ Example::
+ cnf = Cnf("my_parent_cnf", "parent")
+ cnf2 = Cnf("my_child_cnf", "john")
+
+ cnf.add_children(
+ cnf2, # cnf instance directly
+ ("my_child_cnf", "jane", instance=2), # pass a tuple with args
+ ["my_child_cnf", "jack", instance=3]) # pass a list with args
+
+ # adding a child passing arguments of the Cnf constructor
+ cnf.add_child("my_child_cnf", "jane", instance=2)
+ """
+ added_children = CnfList()
+ for c in children:
+ if isinstance(c, Cnf):
+ new_child = self.add_child(c)
+ elif isinstance(c, tuple) or isinstance(c, list):
+ new_child = self.add_child(*c)
+ else:
+ raise ValueError(f"Children item {c} must be either a Cnf, a tuple or a list")
+ added_children.append(new_child)
+ return added_children
+
+ def __eq__(self, other):
+ """
+ Equality implementation.
+
+ :param other: object to compare this instance against
+ :type other: any
+ :returns: whether `other` is equal to this instance
+ :rtype: bool
+
+ This is particularly useful when comparing instances of
+ :py:class:`CnfList`
+ """
+ if not isinstance(other, Cnf):
+ return False
+
+ # NOTE: we try to define two variables as equal in the same way as the
+ # set_cnf binary would if we were passing it an updated CNF variable.
+ # It does not take comments, children and lineno into account when we
+ # pass it a variable; it will rather compare the data we compare here,
+ # and if it finds a match it will update it with the changed children.
+ return self.name == other.name \
+ and self.value == other.value \
+ and self.instance == other.instance \
+ and self.parent == other.parent
+
+ def __str__(self):
+ """
+ Get a string representation of this instance.
+
+ :returns: a string in the cnfvar format
+ :rtype: str
+ """
+ if self.parent is None:
+ this_str = self._PARENT_TEMPLATE.format(
+ lineno=self.lineno,
+ name=self.name.upper(),
+ instance=self.instance,
+ value=self.value
+ )
+ else:
+ depth = 0
+ curr = self
+ while curr.parent is not None:
+ depth += 1
+ curr = curr.parent
+
+ this_str = self._CHILD_TEMPLATE.format(
+ lineno=self.lineno,
+ indent=self._NEST_INDENT * depth,
+ parent=self.parent.lineno,
+ name=self.name.upper(),
+ instance=self.instance,
+ value=self.value
+ )
+
+ if self.comment is not None:
+ this_str += f" # {self.comment}"
+
+ for child in self.children:
+ this_str += f"\n{child}"
+
+ return this_str
+
+ def __repr__(self):
+ """
+ Get a printable representation of this instance.
+
+ :returns: a string in the cnfvar format
+ :rtype: str
+ """
+ repr_ = self._PARENT_TEMPLATE.format(
+ lineno=self.lineno,
+ name=self.name.upper(),
+ instance=self.instance,
+ value=self.value
+ ) if self.parent is None else self._CHILD_TEMPLATE.format(
+ lineno=self.lineno,
+ indent="",
+ parent=self.parent.lineno,
+ name=self.name.upper(),
+ instance=self.instance,
+ value=self.value
+ )
+ return f"Cnf{{ {repr_} [children={len(self.children)}] }}"
+
+
+###############################################################################
+# MIXINS
+###############################################################################
+#
+# These mixins add functionality to the base API without polluting it.
+#
+
+class CnfListSerializationMixin(BaseCnfList):
+ """Add serialization support to BaseCnfList."""
+
+ def to_cnf_structure(self, renumber=True):
+ """
+ Convert this list to an object meaningful to :py:mod:`cnfvar`.
+
+ :param bool renumber: whether to fix up the number/ids of the CNFs
+ :returns: a dictionary with the converted values
+ :rtype: {str, {str, str or int}}
+ """
+ if renumber:
+ self.renumber()
+ return {"cnf": [x.to_cnfvar_dict() for x in self]}
+
+ def to_cnf_file(self, path, renumber=True, encoding=ENCODING):
+ """
+ Dump a string representation of this list in the cnfvar format to a file.
+
+ :param str path: path to the file to write to
+ :param bool renumber: whether to fix the lineno of the cnfvars
+ :param str encoding: encoding to use to save the file
+ """
+ if renumber:
+ self.renumber()
+ with open(path, "w", encoding=encoding) as fp:
+ fp.write(str(self))
+
+ def to_json_string(self, renumber=True):
+ """
+ Generate a JSON representation of this list in the cnfvar format.
+
+ :param bool renumber: whether to fix the lineno of the cnfvars
+ :returns: the JSON string
+ :rtype: str
+ """
+ def _to_dict(cnf):
+ d = {
+ "number": cnf.lineno,
+ "varname": cnf.name,
+ "data": cnf.value,
+ "instance": cnf.instance
+ }
+ if cnf.parent and cnf.parent.lineno:
+ d["parent"] = cnf.parent.lineno
+ if cnf.comment is not None:
+ d["comment"] = cnf.comment
+ if len(cnf.children) > 0:
+ d["children"] = [_to_dict(c) for c in cnf.children]
+ return d
+ if renumber:
+ self.renumber()
+ json_list = [_to_dict(c) for c in self]
+ return json.dumps({"cnf": json_list})
+
+ def to_json_file(self, path, renumber=True):
+ """
+ Dump a JSON representation of this list to a file.
+
+ :param str path: path to the file to write to
+ :param bool renumber: whether to fix the lineno of the cnfvars
+ """
+ with open(path, "w", encoding="utf8") as fp:
+ fp.write(self.to_json_string(renumber=renumber))
+
+ @classmethod
+ def from_cnf_structure(cls, obj):
+ """
+ Create a list from a cnfvar object from the :py:mod:`cnfvar` module.
+
+ :param obj: an object as defined in the :py:mod:`cnfvar`
+ :type obj: {str, {str, str or int}}
+ :returns: a list of cnfvars
+ :rtype: :py:class:`CnfList`
+ """
+ return cls(map(Cnf.from_cnf_structure, obj["cnf"]))
+
+ @classmethod
+ def from_cnf_string(cls, data):
+ """
+ Create a list from a cnfvar string.
+
+ :param str data: string to generate the list from
+ :returns: a list of cnfvars
+ :rtype: :py:class:`CnfList`
+ """
+ cnf_obj = cnfvar_old.read_cnf(data)
+ return CnfList.from_cnf_structure(cnf_obj)
+
+ @classmethod
+ def from_json_string(cls, data):
+ """
+ Create a list from a json string.
+
+ :param str data: string to generate the list from
+ :returns: a list of cnfvars
+ :rtype: :py:class:`CnfList`
+ """
+ cnf_obj = json.loads(data)
+ return CnfList.from_cnf_structure(cnf_obj)
+
+ @classmethod
+ def from_cnf_file(cls, path, encoding=ENCODING):
+ """
+ Create a list from a cnfvar file.
+
+ :param str path: path to the file to read
+ :param str encoding: encoding to use to open the file (defaults to
+ latin1 as this is the default encoding)
+ :returns: a list of cnfvars
+ :rtype: :py:class:`CnfList`
+ """
+ with open(path, "r", encoding=encoding) as fp:
+ return CnfList.from_cnf_string(fp.read())
+
+ @classmethod
+ def from_json_file(cls, path):
+ """
+ Create a list from a json file.
+
+ :param str path: path to the file to read
+ :returns: a list of cnfvars
+ :rtype: :py:class:`CnfList`
+ """
+ with open(path, "r", encoding="utf8") as fp:
+ return CnfList.from_json_string(fp.read())
+
+
+class CnfSerializationMixin(BaseCnf):
+ """Add serialization support to BaseCnf."""
+
+ def to_cnfvar_dict(self):
+ """
+ Convert this instance to dictionary from the :py:mod:`cnfvar` module.
+
+ :returns: the dictionary created
+ :rtype: {str, str or int}
+
+ .. todo:: this method is still needed because dumping cnf variables
+ to strings (json or not) is still delegated to the old cnfvar module.
+ """
+ d = {
+ "number": self.lineno,
+ "varname": self.name,
+ "data": self.value,
+ "instance": self.instance
+ }
+ if self.parent and self.parent.lineno:
+ d["parent"] = self.parent.lineno
+ if self.comment is not None:
+ d["comment"] = self.comment
+ if len(self.children) > 0:
+ d["children"] = [c.to_cnfvar_dict() for c in self.children]
+ return d
+
+ def to_json_string(self, renumber=True):
+ """
+ Convert this instance to a JSON string.
+
+ :param bool renumber: whether to fix the lineno of the cnfvars
+ :returns: the JSON string
+ :rtype: str
+ """
+ return CnfList([self]).to_json_string(renumber=renumber)
+
+ def to_cnf_file(self, path, renumber=True, encoding=ENCODING):
+ """
+ Dump a string representation of this instance to a file.
+
+ :param str path: path to the file to write to
+ :param bool renumber: whether to fix the lineno of this cnfvar and its children
+ :param str encoding: encoding to use to save the file
+ """
+ CnfList([self]).to_cnf_file(path, renumber=renumber, encoding=encoding)
+
+ def to_json_file(self, path, renumber=True):
+ """
+ Dump a JSON representation of this instance to a file.
+
+ :param str path: path to the file to write to
+ :param bool renumber: whether to fix the lineno of this cnfvar and its children
+ """
+ CnfList([self]).to_json_file(path, renumber=renumber)
+
+ @classmethod
+ def from_cnf_structure(cls, obj):
+ """
+ Create an instance from a dictionary from the :py:mod:`cnfvar` module.
+
+ :param obj: dictionary to convert to this instance
+ :type obj: {str, str or int}
+ :returns: the cnf instance created
+ :rtype: :py:class:`Cnf`
+ """
+ cnf = Cnf(obj["varname"], obj["data"],
+ instance=obj["instance"], lineno=obj["number"],
+ comment=obj.get("comment", None))
+ for ch_obj in obj.get("children", []):
+ child_cnf = Cnf.from_cnf_structure(ch_obj)
+ cnf.add_child(child_cnf)
+ return cnf
+
+ @classmethod
+ def from_cnf_string(cls, data):
+ """
+ Create an instance of this class from a cnfvar string.
+
+ :param str data: cnfvar string to convert
+ :returns: the cnf instance created
+ :rtype: :py:class:`Cnf`
+ """
+ return CnfListSerializationMixin.from_cnf_string(data).single()
+
+ @classmethod
+ def from_json_string(cls, data):
+ """
+ Create an instance of this class from a JSON string.
+
+ :param str data: JSON string to convert
+ :returns: the cnf instance created
+ :rtype: :py:class:`Cnf`
+ """
+ return CnfListSerializationMixin.from_json_string(data).single()
+
+ @classmethod
+ def from_cnf_file(cls, path, encoding=ENCODING):
+ """
+ Create an instance of this class from a cnfvar file.
+
+ :param str path: path to the file to read
+ :param str encoding: encoding to use to read the file
+ :returns: the cnf instance created
+ :rtype: :py:class:`Cnf`
+ """
+ return CnfListSerializationMixin.from_cnf_file(path, encoding=encoding).single()
+
+ @classmethod
+ def from_json_file(cls, path):
+ """
+ Create an instance of this class from a json file.
+
+ :param str path: path to the file to read
+ :returns: the cnf instance created
+ :rtype: :py:class:`Cnf`
+ """
+ return CnfListSerializationMixin.from_json_file(path).single()
+
+
+class CnfListArniedApiMixin(BaseCnfList):
+ """Add support for converting this class to and from Arnied API classes."""
+
+ def to_api_structure(self):
+ """
+ Convert this list to the corresponding object in the arnied API.
+
+ :returns: the converted object
+ :rtype: [:py:class:`arnied_api.CnfVar`]
+ """
+ return [c.to_api_structure() for c in self]
+
+ @classmethod
+ def from_api_structure(cls, cnfvar_list):
+ """
+ Convert a list from the arnied API into a list of this type.
+
+ :param cnfvar_list: list to convert
+ :type cnfvar_list: [:py:class:`arnied_api.CnfVar`]
+ :returns: the list created
+ :rtype: :py:class:`CnfList`
+ """
+ return CnfList((Cnf.from_api_structure(c) for c in cnfvar_list),
+ renumber=True)
+
+
+class CnfArniedApiMixin(BaseCnf):
+ """Add support for converting this class to and from Arnied API classes."""
+
+ def to_api_structure(self):
+ """
+ Convert this instance to the corresponding object in the arnied API.
+
+ :returns: the converted object
+ :rtype: :py:class:`arnied_api.CnfVar`
+ """
+ return arnied_api.CnfVar(
+ self.name.upper(),
+ self.instance,
+ self.value,
+ False, # default here to False
+ children=[c.to_api_structure() for c in self.children])
+
+ @classmethod
+ def from_api_structure(cls, cnfobj):
+ """
+ Convert an object from the arnied API into an instance of this type.
+
+ :param cnfobj: object to convert
+ :type cnfobj: :py:class:`arnied_api.CnfVar`
+ :returns: the instance created
+ :rtype: :py:class:`Cnf`
+ """
+ cnf = Cnf(cnfobj.name, cnfobj.data, cnfobj.instance)
+ children = CnfList((Cnf.from_api_structure(c) for c in cnfobj.children))
+ for c in children:
+ c.parent = cnf
+ cnf.children.extend(children)
+ return cnf
+
+
+class CnfShortcutsMixin(BaseCnf):
+ """Extend the base CNF class with useful methods."""
+
+ def enable(self):
+ """Treat this variable as a boolean var and set its value to 1."""
+ self.value = "1"
+
+ def disable(self):
+ """Treat this variable as a boolean var and set its value to 0."""
+ self.value = "0"
+
+ def is_enabled(self):
+ """Treat this variable as a boolean var and check if its value is 1."""
+ return self.value == "1"
+
+ def enable_child_flag(self, name):
+ """
+ Set the value of the child CNF matching `name` to "1".
+
+ :param str name: name of the child whose value to enable
+
+ .. note:: child will be created if it does not exist.
+ """
+ cnf = self.children.first_with_name(name, default=None)
+ if cnf is None:
+ self.add_child(name, "1")
+ else:
+ cnf.enable()
+
+ def disable_child_flag(self, name):
+ """
+ Set the value of the child CNF matching `name` to "0".
+
+ :param str name: name of the child whose value to disable
+
+ .. note:: child will be created if it does not exist.
+ """
+ cnf = self.children.first_with_name(name, default=None)
+ if cnf is None:
+ self.add_child(name, "0")
+ else:
+ cnf.disable()
+
+ def child_flag_enabled(self, name):
+ """
+ Check if a given child has a value equal to `1`.
+
+ :param str name: name of the child to check
+ :returns: whether the value of the given child, if it exists, is 1
+ :rtype: bool
+ """
+ cnf = self.children.first_with_name(name, default=None)
+ return cnf.is_enabled() if cnf is not None else False
+
+
+class CnfListQueryingMixin(BaseCnfList):
+ """Mixing adding shortcuts for common filter operations."""
+
+ def single(self, where_filter=None, default=DEFAULT):
+ """
+ Get the only CNF of this list or raise if none or more than one exist.
+
+ :param where_filter: predicate to apply against CNFs beforehand
+ :type where_filter: function accepting a CNF and returning a boolean
+ :param default: value to return in case the list is empty
+ :type default: any
+ :raises: :py:class:`ValueError` if a single value cannot be found and
+ a default value was not specified
+ :returns: the first and only element of this list, or default if set
+ and no element is present
+ :rtype: :py:class:`Cnf`
+ """
+ list_ = self.where(where_filter) if where_filter is not None else self
+
+ if len(list_) == 1:
+ return list_[0]
+ elif len(list_) == 0 and default != DEFAULT:
+ return default
+ else:
+ raise ValueError(f"CnfList does not contain a single item (len={len(list_)})")
+
+ def first(self, where_filter=None, default=DEFAULT):
+ """
+ Get the first element in this list or raise if the list is empty.
+
+ :param where_filter: predicate to apply against CNFs beforehand
+ :type where_filter: function accepting a CNF and returning a boolean
+ :param default: value to return in case the list is empty
+ :type default: any
+ :raises: :py:class:`ValueError` if a single value cannot be found and
+ a default value was not specified
+ :returns: the first element of this list, or default if set and
+ no element is present
+ :rtype: :py:class:`Cnf`
+ """
+ list_ = self.where(where_filter) if where_filter is not None else self
+ if len(list_) > 0:
+ return list_[0]
+ elif default != DEFAULT:
+ return default
+ else:
+ raise ValueError("Cannot get the first item - CnfList is empty")
+
+ def with_value(self, value):
+ """Shortcut method for filtering by value."""
+ return self.where(lambda c: c.value == value)
+
+ def with_name(self, name):
+ """Shortcut method for filtering by name."""
+ return self.where(lambda c: c.name == name)
+
+ def with_instance(self, instance):
+ """Shortcut method for filtering by instance."""
+ return self.where(lambda c: c.instance == instance)
+
+ def single_with_name(self, name, default=DEFAULT):
+ """Shortcut method for getting the single item with a given name."""
+ return self.with_name(name).single(default=default)
+
+ def single_with_value(self, value, default=DEFAULT):
+ """Shortcut method for getting the single item with a given value."""
+ return self.with_value(value).single(default=default)
+
+ def single_with_instance(self, instance, default=DEFAULT):
+ """Shortcut method for getting the single item with a given instance."""
+ return self.with_instance(instance).single(default=default)
+
+ def first_with_name(self, name, default=DEFAULT):
+ """Shortcut method for getting the first item with a given name."""
+ return self.with_name(name).first(default=default)
+
+ def first_with_value(self, value, default=DEFAULT):
+ """Shortcut method for getting the first item with a given value."""
+ return self.with_value(value).first(default=default)
+
+ def first_with_instance(self, instance, default=DEFAULT):
+ """Shortcut method for getting the first item with a given instance."""
+ return self.with_instance(instance).first(default=default)
+
+
+###############################################################################
+# PUBLIC CLASSES
+###############################################################################
+#
+# Set up the classes with the mixins we want to be available by default.
+#
+
+
+class CnfList(CnfListSerializationMixin, CnfListArniedApiMixin, CnfListQueryingMixin):
+ """Collection of Cnf variables."""
+
+ pass
+
+
+class Cnf(CnfSerializationMixin, CnfArniedApiMixin, CnfShortcutsMixin):
+ """Class representing a cnfvar."""
+
+ pass
+
+
+__all__ = ["CnfList", "Cnf"]
--- /dev/null
+# The software in this package is distributed under the GNU General
+# Public License version 2 (with a special exception described below).
+#
+# A copy of GNU General Public License (GPL) is included in this distribution,
+# in the file COPYING.GPL.
+#
+# As a special exception, if other files instantiate templates or use macros
+# or inline functions from this file, or you compile this file and link it
+# with other works to produce a work based on this file, this file
+# does not by itself cause the resulting work to be covered
+# by the GNU General Public License.
+#
+# However the source code for this file must still be made available
+# in accordance with section (3) of the GNU General Public License.
+#
+# This exception does not invalidate any other reasons why a work based
+# on this file might be covered by the GNU General Public License.
+#
+# Copyright (c) 2016-2022 Intra2net AG <info@intra2net.com>
+
+"""
+store: implementations of CNF stores using varlink and `*et_cnf` binaries.
+
+Featuring:
+ - CnfStore: the main store class that is implemented using the varlink API
+ - BinaryCnfStore: alternative store class implementation using the `set_cnf`
+ and `get_cnf` binaries
+
+.. seealso:: Overview Diagram linked to from doc main page
+
+.. codeauthor:: Intra2net
+"""
+import subprocess
+import logging
+import time
+import html
+
+from .. import arnied_wrapper, arnied_api
+from . import CnfBinary, Cnf, CnfList
+
+log = logging.getLogger("pyi2ncommon.cnfvar.store")
+
+
+class CnfStore:
+ """Main CNF store class that uses the varlink API."""
+
+ def __init__(self, backend_driver=arnied_api.Arnied):
+ """
+ Initialize this class.
+
+ :param backend_driver: driver to use to talk to the cnfvar backend
+ :type: :py:class:`arnied_api.Arnied`
+ """
+ self._driver = backend_driver
+ log.debug("Initialized CnfStore with driver `%s`", type(backend_driver))
+
+ def query(self, name=None, instance=None):
+ """
+ Query the CNF store and return a list of parsed CNFs.
+
+ :param str name: optional name of the CNFs to query
+ :param instance: optional CNF instance
+ :type instance: str or int
+ :returns: list of parsed CNF values
+ :rtype: :py:class:`CnfList`
+
+ Example::
+ store = CnfStore()
+ user_cnfs = store.query("USER")
+ """
+ log.debug("Querying CnfStore with name=%s and instance=%s via arnied API",
+ name, instance)
+
+ # the API only expects an object if there is a name (and it's not case-insensitive)
+ query = arnied_api.GetCnfQuery(name.upper(), instance) if name else None
+ api_ret = self._driver.get_cnf(query)
+
+ # NOTE: logging all output here would result in huge lines when querying
+ # all variables via `store.query()`
+ log.debug("Arnied API returned %d cnfvars", len(api_ret.vars))
+ return CnfList.from_api_structure(api_ret.vars)
+
+ def commit(self, cnf, fix_problems=False):
+ """
+ Update or insert CNF variables from a list.
+
+ :param cnf: CNF instance or list of CNFs to update or insert
+ :type cnf: :py:class:`Cnf` or :py:class:`CnfList`
+ :raises: :py:class:`CommitException` if the arnied API complains
+ :param bool fix_problems: whether to automatically fix errors in the vars
+
+ .. note:: you can mix variables to insert and variables to update
+ in the same list as the system should handle it nicely
+
+ Example::
+ store = CnfStore()
+ user_cnf = store.query("USER")\
+ .single_with_value("joe")
+ user_cnf.add_child("user_group_member_ref", "3")
+ store.commit(user_cnf)
+ """
+ cnf = self._cnf_or_list(cnf, operation="commit")
+ self._autofix_instances(cnf)
+ log.debug("Committing variables via arnied API:\n%s", cnf)
+
+ arnied_cnfs = cnf.to_api_structure()
+ self._do_commit(cnf, arnied_cnfs, fix_problems=fix_problems)
+
+ def delete(self, cnf, fix_problems=False):
+ """
+ Delete a list of top-level CNF variables.
+
+ :param cnf: a single CNF value or a list of values
+ :type cnf: :py:class:`CnfList` or :py:class:`Cnf`
+ :param bool fix_problems: whether to automatically fix errors in the vars
+
+ Example::
+ store = CnfStore()
+ user_cnfs = store.query("USER")\
+ .where(lambda c: c.value in ["joe", "jane"])
+ store.delete(user_cnfs)
+ """
+ cnf = self._cnf_or_list(cnf, operation="delete")
+ if any((c.parent is not None for c in cnf)):
+ raise RuntimeError("Calling delete is only supported on top-level CNF variables")
+ log.debug("Deleting variables via arnied API:\n%s", cnf)
+
+ arnied_cnfs = cnf.to_api_structure()
+ for c in arnied_cnfs:
+ c.deleted = True
+ c.children.clear()
+ self._do_commit(cnf, arnied_cnfs, fix_problems=fix_problems)
+
+ def _autofix_instances(self, cnfs):
+ """
+ Auto-assign a valid instance value to all top-level vars in a list.
+
+ :param cnfs: list of cnfvars to fix
+ :type cnfs: :py:class`CnfList`
+
+ When the instance of a cnfvar is -1, the backend initializes it
+ automatically. However it starts on 1, whereas many variables are not
+ allowed to start on one (those that are meant to be unique, f.e.).
+ This method can be used in child classes to use an alternative scheme,
+ however for performance reasons the base API class uses the default and
+ relies on the cnfvar backend to do this job.
+ """
+
+ def _do_commit(self, original_cnfs, arnied_cnfs, fix_problems=False):
+ """
+ Set cnfvars and commit changes.
+
+ :param original_cnfs: list of CNFs to print in case of errors
+ :type original_cnfs: :py:class:`CnfList`
+ :param arnied_cnfs: list of cnfvars to pass to the arnied API
+ :type arnied_cnfs: [:py:class:`arnied_api.CnfVar`]
+ :param bool fix_problems: whether to automatically fix errors in the vars
+ :raises: :py:class:`CommitException` if the arnied API complains
+ """
+ try:
+ ret = self._driver.set_commit_cnf(vars=arnied_cnfs, username=None,
+ nogenerate=False,
+ fix_commit=fix_problems)
+ except arnied_api.CnfCommitError as ex:
+ # fatal errors, we will handle it in our custom exception
+ ret = ex
+
+ errors = []
+ for r in ret.results:
+ # message can contain HTML escape codes
+ msg = html.unescape(r.result_msg)
+
+ # `result_type` is defined as int in the varlink API,
+ # but the meaning of the codes are:
+ # enum result_type { OK=0, WARN=1, FAIL_TEMP=2, FAIL=3 }
+ if r.result_type == 1:
+ log.debug("Warning in `` %s,%s: \"%s\" ``: {msg} (code=%s)",
+ r.name, r.instance, r.data, r.result_type)
+ elif r.result_type > 1:
+ errors.append(f"Error in `` {r.name},{r.instance}: \"{r.data}\" ``"
+ f": {msg}")
+
+ if len(errors) > 0:
+ log.debug("Error sending variables:\n%s", arnied_cnfs)
+ raise CommitException(original_cnfs, "\n".join(errors))
+
+ self._wait_for_generate()
+
+ def _wait_for_generate(self, timeout=300):
+ """
+ Wait for the 'generate' program to end.
+
+ :param int timeout: program run timeout
+ :raises: :py:class:`TimeoutError` if the program did not finish on time
+ """
+ def scheduled_or_running(progname):
+ ret = self._driver.is_scheduled_or_running(progname)
+ return ret.status in [arnied_api.ProgramStatus.Scheduled,
+ arnied_api.ProgramStatus.Running]
+
+ def wait_for_program(progname):
+ log.debug("Waiting for `%s` to be running", progname)
+ for _ in range(10):
+ if scheduled_or_running(progname):
+ # if is running or scheduled, break to wait for completion
+ break
+ time.sleep(1)
+ else:
+ # after trying and retrying, program is not scheduled nor
+ # running, so it is safe to assume it has already executed
+ return
+
+ # program running or scheduled, wait
+ log.debug("Waiting for `%s` to finish", progname)
+ for _ in range(0, timeout):
+ if not scheduled_or_running(progname):
+ # finished executing, bail out
+ return
+ time.sleep(1)
+ raise TimeoutError(f"Program `{progname}` did not end in time")
+
+ wait_for_program("GENERATE")
+ wait_for_program("GENERATE_OFFLINE")
+
+ def _cnf_or_list(self, cnf, operation) -> CnfList:
+ """
+ Validate and wrap a CNF value into a list.
+
+ :param cnf: a single CNF value or a list of values
+ :type cnf: :py:class:`CnfList` or :py:class:`Cnf`
+ :param str operation: name of the operation that is being done
+ :raises: :py:class:`TypeError` if the type of the CNF object
+ is neither a list nor a CNF value
+ :returns: wrapped CNF value
+ :rtype: :py:class:`CnfList`
+ """
+ if isinstance(cnf, Cnf):
+ cnf = CnfList([cnf])
+ elif not isinstance(cnf, CnfList):
+ raise TypeError(f"Cannot {operation} value(s) of type `{type(cnf)}`")
+ return cnf
+
+
+class BinaryCnfStore(CnfStore):
+ """Implementation of the CNF store that uses `get_cnf` and `set_cnf`."""
+
+ #: how much to wait for arnied to report running
+ ARNIED_TIMEOUT = 30
+
+ def __init__(self, backend_driver=CnfBinary):
+ """
+ Initialize this class.
+
+ :param backend_driver: driver to use to talk to the cnfvar backend
+ :type: :py:class:`CnfBinary`
+ """
+ super().__init__(backend_driver=backend_driver)
+ log.debug("Initialized BinaryCnfStore with driver `%s`", type(backend_driver))
+
+ def query(self, name=None, instance=None):
+ """
+ Query the CNF store and return a list of parsed CNFs.
+
+ :param str name: optional name of the CNFs to query
+ :param instance: optional CNF instance
+ :type instance: str or int
+ :returns: list of parsed CNF values
+ :rtype: :py:class:`CnfList`
+
+ Example::
+ store = CnfStore()
+ user_cnfs = store.query("USER")
+ """
+ log.debug("Querying BinaryCnfStore with name=%s and instance=%s",
+ name, instance)
+ output = self._driver.get_cnf(name, instance=instance)
+
+ if len(output) == 0:
+ # otherwise cnfvar raises a generic Malformed exception
+ return CnfList()
+
+ return CnfList.from_cnf_string(output)
+
+ def commit(self, cnf, fix_problems=False):
+ """
+ Update or insert CNF variables from a list.
+
+ :param cnf: CNF instance or list of CNFs to update or insert
+ :type cnf: :py:class:`Cnf` or :py:class:`CnfList`
+ :param bool fix_problems: whether to automatically fix errors in the vars
+
+ .. note:: you can mix variables to insert and variables to update
+ in the same list as the system should handle it nicely
+
+ Example::
+ store = CnfStore()
+ user_cnf = store.query("USER")\
+ .single_with_value("joe")
+ user_cnf.add_child("user_group_member_ref", "3")
+ store.commit(user_cnf)
+ """
+ cnf = self._cnf_or_list(cnf, operation="commit")
+ self._autofix_instances(cnf)
+
+ # set_cnf is demanding on lineno's
+ cnf.renumber()
+ log.debug("Committing variables via binaries:\n%s", cnf)
+
+ self._call_arnied(arnied_wrapper.verify_running, timeout=self.ARNIED_TIMEOUT)
+ try:
+ self._driver.set_cnf(input_str=str(cnf), fix_problems=fix_problems)
+ except subprocess.CalledProcessError as ex:
+ raise CommitException(cnf, ex.stderr) from None
+ self._call_arnied(arnied_wrapper.wait_for_generate)
+
+ def delete(self, cnf, fix_problems=False):
+ """
+ Delete a list of top-level CNF variables.
+
+ :param cnf: a single CNF value or a list of values
+ :type cnf: :py:class:`CnfList` or :py:class:`Cnf`
+ :param bool fix_problems: whether to automatically fix errors in the vars
+
+ Example::
+ store = CnfStore()
+ user_cnfs = store.query("USER")\
+ .where(lambda c: c.value in ["joe", "jane"])
+ store.delete(user_cnfs)
+ """
+ cnf = self._cnf_or_list(cnf, operation="delete")
+ if any((c.parent is not None for c in cnf)):
+ raise RuntimeError("Calling delete is only supported on top-level CNF variables")
+
+ # set_cnf is demanding on lineno's
+ cnf.renumber()
+ log.debug("Deleting variables via binaries:\n%s", cnf)
+
+ self._call_arnied(arnied_wrapper.verify_running, timeout=self.ARNIED_TIMEOUT)
+ try:
+ self._driver.set_cnf(input_str=str(cnf), delete=True, fix_problems=fix_problems)
+ except subprocess.CalledProcessError as ex:
+ raise CommitException(cnf, ex.stderr) from None
+ self._call_arnied(arnied_wrapper.wait_for_generate)
+
+ def _call_arnied(self, fn, *args, **kwargs):
+ """
+ Simple proxy around the arnied to be overwritten in child classes.
+
+ :param fn: function to invoke in the arnied wrapper
+ :type fn: py:function
+ :param args: arguments to be passed to function
+ :param kwargs: named arguments to be passed to function
+ """
+ return fn(*args, **kwargs)
+
+
+#: pattern for more verbose error message for :py:class:`CommitException`
+COMMIT_EXCEPTION_MESSAGE = """\
+Error committing CNF variables!
+----------------------------
+Input:
+{cnfvars}
+----------------------------
+Error:
+{msg}
+"""
+
+
+class CommitException(Exception):
+ """Custom exception for commit errors."""
+
+ def __init__(self, cnfvars, msg):
+ """
+ Initialize this exception.
+
+ :param cnfvars: list of CNF variables that would be committed
+ :type cnfvars: CnfList
+ :param str msg: error message
+ """
+ super().__init__(msg)
+ self.message = COMMIT_EXCEPTION_MESSAGE.format(cnfvars=cnfvars, msg=msg)
-------------------------------------------------------------------------------
Represent CNF_VARs as recursive structures.
+DEPRECATED! Please do not extend this or add new uses of this module, use
+:py:mod:`pyi2ncommon.arnied_api` or :py:mod:`pyi2ncommon.cnfvar` instead!
+
Copyright: 2014-2017 Intra2net AG
License: GPLv2+
import re
import io
-#
-# helpers
-#
-# Sadly, the Intranator is still stuck with one leg in the 90s.
-#
+###############################################################################
+# CONSTANTS
+###############################################################################
+
+
+CNF_FIELD_MANDATORY = set ([ "varname", "data", "instance" ])
+CNF_FIELD_OPTIONAL = set ([ "parent", "children", "comment", "number" ])
+CNF_FIELD_KNOWN = CNF_FIELD_MANDATORY | CNF_FIELD_OPTIONAL
+grab_parent_pattern = re.compile(b"""
+ ^ # match from start
+ \s* # optional spaces
+ \d+ # line number
+ \s+ # spaces
+ \((\d+)\) # parent
+ """,
+ re.VERBOSE)
+base_line_pattern = re.compile(b"""
+ ^ # match from start
+ \s* # optional spaces
+ (\d+) # line number
+ \s+ # spaces
+ ([A-Z][A-Z0-9_]*) # varname
+ \s* # optional spaces
+ , # delimiter
+ \s* # optional spaces
+ (-1|\d+) # instance
+ \s* # optional spaces
+ : # delimiter
+ \s* # optional spaces
+ \"( # quoted string (data)
+ (?: \\\" # (of escaped dquote
+ |[^\"])* # or anything not a
+ )\" # literal quote)
+ \s* # optional spaces
+ ( # bgroup
+ \# # comment leader
+ \s* # optional spaces
+ .* # string (comment)
+ )? # egroup, optional
+ $ # eol
+ """,
+ re.VERBOSE)
+
+child_line_pattern = re.compile(b"""
+ ^ # match from start
+ \s* # optional spaces
+ (\d+) # line number
+ \s+ # spaces
+ \((\d+)\) # parent
+ \s+ # spaces
+ ([A-Z][A-Z0-9_]*) # varname
+ \s* # optional spaces
+ , # delimiter
+ \s* # optional spaces
+ (-1|\d+) # instance
+ \s* # optional spaces
+ : # delimiter
+ \s* # optional spaces
+ \"([^\"]*)\" # quoted string (data)
+ \s* # optional spaces
+ ( # bgroup
+ \# # comment leader
+ \s* # optional spaces
+ .* # string (comment)
+ )? # egroup, optional
+ $ # eol
+ """,
+ re.VERBOSE)
+
+
+###############################################################################
+# HELPERS
+###############################################################################
+
+
+#
+# Sadly, the Intranator is still stuck with one leg in the 90s.
+#
def to_latin1(s):
"""Take given unicode str and convert it to a latin1-encoded `bytes`."""
return s.encode("latin-1")
"""Take given latin1-encoded `bytes` value and convert it to `str`."""
return s.decode("latin-1")
+
+#
+# Conversion functions
+#
+
+def marshal_in_number(number):
+ return int(number)
+
+
+def marshal_in_parent(parent):
+ return int(parent)
+
+
+def marshal_in_instance(instance):
+ return int(instance)
+
+
+def marshal_in_varname(varname):
+ return from_latin1(varname).lower()
+
+
+def marshal_in_data(data):
+ return from_latin1(data) if data is not None else ""
+
+
+def marshal_in_comment(comment):
+ return comment and from_latin1(comment[1:].strip()) or None
+
+
#
-# traversal
+# Type checking
#
+def is_string(s):
+ return isinstance(s, str)
+
+
+###############################################################################
+# EXCEPTIONS
+###############################################################################
+
class InvalidCNF(Exception):
return "Malformed CNF_VAR: \"%s\"" % self.msg
-def walk_cnf(cnf, nested, fun, acc):
- """
- :type cnf: cnf list
- :type nested: bool
- :type fun: 'a -> bool -> (cnf stuff) -> 'a
- :type acc: 'a
- :rtype: 'a
- """
- for var in cnf:
- acc = fun(acc,
- nested,
- comment=var.get("comment", None),
- data=var.get("data", None),
- instance=var.get("instance", None),
- number=var.get("number", None),
- parent=var.get("parent", None),
- varname=var.get("varname", None))
- children = var.get("children", None)
- if children is not None:
- acc = walk_cnf(children, True, fun, acc)
- return acc
+class MalformedCNF(Exception):
+ def __init__(self, msg):
+ self.msg = msg
-#
-# validation
-#
+ def __str__(self):
+ return "Malformed CNF file: \"%s\"" % self.msg
-def is_string(s):
- return isinstance(s, str)
+
+###############################################################################
+# VALIDATION
+###############################################################################
def is_valid(acc,
well-formed member of the argument will cause the predicate to bail out
with an exception during traversal.
"""
- cnf = cnf_root (root)
+ cnf = cnf_root(root)
if cnf is None:
- raise InvalidCNF (root)
+ raise InvalidCNF(root)
return walk_cnf(cnf, False, is_valid, {}) is not None
-def count_vars (root):
+def is_cnf_var(obj):
"""
- Traverse the cnf structure recursively, counting VAR objects (CNF lines).
+ Check whether a dictionary is a valid CNF.
+
+ :param dict obj: dictionary to check
+ :returns: True if the dictionary has all the mandatory fields and no
+ unknown fields, False otherwise
+ :rtype: bool
"""
- cnf = cnf_root (root)
- if cnf is None:
- raise InvalidCNF (root)
- return walk_cnf (cnf, True, lambda n, _nested, **_kwa: n + 1, 0)
+ assert isinstance (obj, dict)
+
+ for f in CNF_FIELD_MANDATORY:
+ if obj.get(f, None) is None:
+ return False
+
+ for f in obj:
+ if f not in CNF_FIELD_KNOWN:
+ return False
+
+ return True
+
+
+###############################################################################
+# DESERIALIZATION
+###############################################################################
+
#
-# deserialization
+# JSON reader for get_cnf -j (the easy part)
#
-# the easy part: JSON reader for get_cnf -j
-
def make_varname_lowercase(cnfvar):
"""
Custom hook for json decoder: convert variable name to lowercase.
raise TypeError("Invalid CNF_VAR.")
return cnf_json
-# the moderately more complicated part: CNF reader
+#
+# CNF reader (the moderately more complicated part)
+#
+# Parsing usually starts from the `read_cnf`, which accepts a string containing
+# the variables to parse in the same structure as returned by `get_cnf`.
+#
+# In the `prepare` function the string is split into lines, and a 3-element
+# tuple is built. The first (named `current`) and second (named `next`)
+# elements of this tuple are respectively the first and second non-empty lines
+# of the input, while the third is a list of the remaining lines. This tuple is
+# named `state` in the implementation below, and it is passed around during
+# parsing. The `get` and `peek` functions are used to easily retrieve the
+# `current` and `next` items from the "state".
+#
+# When we "advance" the state, we actually drop the "current" element,
+# replacing it with the "next", while a new "next" is popped from the list of
+# remaining lines. Parsing is done this way because we need to look ahead at
+# the next line -- if it is a child it needs to be appended to the `children`
+# property of the current line.
+#
+# Regular expressions are used to extract important information from the CNF
+# lines. Finally, once parsing is completed, a dictionary is returned. The dict
+# has the same structure as the serialized JSON output returned by
+# `get_cnf -j`.
+#
-def advance(cns):
- current, next, stream = cns
- if next is None: # reached end of stream
- return None
- current = next
- try:
- next = stream.pop()
- next = next.strip()
- except IndexError:
- next = None
+def read_cnf(data):
+ """
+ Read cnf data from data bytes.
- if current == "": # skip blank lines
- return advance((current, next, stream))
- return (current, next, stream)
+ :param bytes data: raw data
+ :return: the parsed cnf data
+ :rtype: {str, {str, str or int}}
+ """
+ if isinstance(data, str):
+ data = to_latin1(data)
+ state = prepare(data)
+ if state is None:
+ raise InvalidCNF("Empty input string.")
+
+ cnf = parse_cnf_root(state)
+ if is_cnf(cnf) is False:
+ raise TypeError("Invalid CNF_VAR.")
+ return {"cnf": cnf}
def prepare(raw):
"""
+ Build 3-element iterable from a CNF string dump.
+
+ :param raw: string content as returned by `get_cnf`
:type raw: bytes
+ :returns: 3-element tuple, where the first two elements are the first two
+ lines of the output and the third is a list containing the rest
+ of the lines in reverse.
:rtype: (str * str option * str list) option
"""
lines = raw.splitlines()
return (first, second, lines)
-def peek(cns):
- _, next, _ = cns
- return next
+def advance(cns):
+ """
+ Pop the next line from the stream, advancing the tuple.
+
+ :param cns: a 3-element tuple containing two CNF lines and a list of the
+ remaining lines
+ :type cnd: (str, str, [str])
+ :returns: a new tuple with a new item popped from the list of lines
+ :rtype cnd: (str, str, [str])
+ """
+ current, next, stream = cns
+ if next is None: # reached end of stream
+ return None
+ current = next
+
+ try:
+ next = stream.pop()
+ next = next.strip()
+ except IndexError:
+ next = None
+
+ if current == "": # skip blank lines
+ return advance((current, next, stream))
+ return (current, next, stream)
def get(cns):
+ """
+ Get the current line from the state without advancing it.
+
+ :param cns: a 3-element tuple containing two CNF lines and a list of the
+ remaining lines
+ :type cnd: (str, str, [str])
+ :returns: the CNF line stored as `current`
+ :rtype: str
+ """
current, _, _ = cns
return current
-class MalformedCNF(Exception):
+def peek(cns):
+ """
+ Get the next line from the state without advancing it.
- def __init__(self, msg):
- self.msg = msg
+ :param cns: a 3-element tuple containing two CNF lines and a list of the
+ remaining lines
+ :type cnd: (str, str, [str])
+ :returns: the CNF line stored as `next`
+ :rtype: str
+ """
+ _, next, _ = cns
+ return next
- def __str__(self):
- return "Malformed CNF file: \"%s\"" % self.msg
-grab_parent_pattern = re.compile(b"""
- ^ # match from start
- \d+ # line number
- \s+ # spaces
- \((\d+)\) # parent
- """,
- re.VERBOSE)
+def parse_cnf_root(state):
+ """
+ Iterate over and parse a list of CNF lines.
+
+ :param state: a 3-element tuple containing two lines and a list of the
+ remaining lines
+ :type state: (str, str, [str])
+ :returns: a list of parsed CNF variables
+ :rtype: [dict]
+
+ The function will parse the first element from the `state` tuple, then read
+ the next line to see if it is a child variable. If it is, it will be
+ appended to the last parsed CNF, otherwise top-level parsing is done
+ normally.
+ """
+ lines = []
+ current = get(state)
+ while state:
+ cnf_line = read_base_line(current)
+ if cnf_line is not None:
+ lines.append(cnf_line)
+ state = advance(state)
+ if state is None: # -> nothing left to do
+ break
+ current = get(state)
+ parent = get_parent(current) # peek at next line
+ if parent is not None: # -> recurse into children
+ (state, children, _parent) = parse_cnf_children(state, parent)
+ cnf_line["children"] = children
+ if state is None:
+ break
+ current = get(state)
+ else:
+ state = advance(state)
+ if state is None:
+ break
+ current = get(state)
+ return lines
+
+
+def parse_cnf_children(state, parent):
+ """
+ Read and parse child CNFs of a given parent until there is none left.
+
+ :param state: a 3-element tuple containing two lines and a list of the
+ remaining lines
+ :type state: (str, str, [str])
+ :param parent: id of the parent whose children we are looking for
+ :type parent: int
+ :returns: a 3-element tuple with the current state, a list of children of
+ the given parent and the parent ID
+ :rtype: (tuple, [str], int)
+
+ The function will recursively parse child lines from the `state` tuple
+ until one of these conditions is satisfied:
+
+ 1. the input is exhausted
+ 2. the next CNF line
+ 2.1. is a toplevel line
+ 2.2. is a child line whose parent has a lower parent number
+
+ Conceptually, 2.1 is a very similar to 2.2 but due to the special status of
+ toplevel lines in CNF we need to handle them separately.
+
+ Note that since nesting of CNF vars is achieved via parent line numbers,
+ lines with different parents could appear out of order. libcnffile will
+ happily parse those and still assign children to the specified parent:
+
+ ::
+ # set_cnf <<THATSALL
+ 1 USER,1337: "l33t_h4x0r"
+ 2 (1) USER_GROUP_MEMBER_REF,0: "2"
+ 4 USER,1701: "picard"
+ 5 (4) USER_GROUP_MEMBER_REF,0: "2"
+ 6 (4) USER_PASSWORD,0: "engage"
+ 3 (1) USER_PASSWORD,0: "hacktheplanet"
+ THATSALL
+ # get_cnf user 1337
+ 1 USER,1337: "l33t_h4x0r"
+ 2 (1) USER_GROUP_MEMBER_REF,0: "2"
+ 3 (1) USER_PASSWORD,0: "hacktheplanet"
+ # get_cnf user 1701
+ 1 USER,1701: "picard"
+ 2 (1) USER_GROUP_MEMBER_REF,0: "2"
+ 3 (1) USER_PASSWORD,0: "engage"
+
+ It is a limitation of ``cnfvar.py`` that it cannot parse CNF data
+ structured like the above example: child lists are only populated from
+ subsequent CNF vars using the parent number solely to track nesting levels.
+ The parser does not keep track of line numbers while traversing the input
+ so it doesn’t support retroactively assigning a child to anything else but
+ the immediate parent.
+ """
+ lines = []
+ current = get(state)
+ while True:
+ cnf_line = read_child_line(current)
+ if cnf_line is not None:
+ lines.append(cnf_line)
+ state = advance(state)
+ if state is None:
+ break
+ current = get(state)
+ new_parent = get_parent(current)
+ if new_parent is None:
+ # drop stack
+ return (state, lines, None)
+ if new_parent > parent:
+ # parent is further down in hierarchy -> new level
+ (state, children, new_parent) = \
+ parse_cnf_children (state, new_parent)
+ if state is None:
+ break
+ cnf_line["children"] = children
+ current = get(state)
+ new_parent = get_parent(current)
+ if new_parent is None:
+ # drop stack
+ return (state, lines, None)
+ if new_parent < parent:
+ # parent is further up in hierarchy -> pop level
+ return (state, lines, new_parent)
+ # new_parent == parent -> continue parsing on same level
+ return (state, lines, parent)
def get_parent(line):
+ """
+ Extract the ID of the parent for a given CNF line.
+
+ :param str line: CNF line
+ :returns: parent ID or None if no parent is found
+ :rtype: int or None
+ """
match = re.match(grab_parent_pattern, line)
if match is None: # -> no parent
return None
return int(match.groups()[0])
-def marshal_in_number (number): return int (number)
-
-def marshal_in_parent (parent): return int (parent)
-
-def marshal_in_varname (varname): return from_latin1 (varname).lower ()
-
-def marshal_in_instance (instance): return int (instance)
-
-def marshal_in_data (data): return from_latin1 (data) if data is not None else ""
-
-def marshal_in_comment (comment): return comment and from_latin1(comment[1:].strip()) or None
+def read_base_line(line):
+ """
+ Turn one top-level CNF line into a dictionary.
-base_line_pattern = re.compile(b"""
- ^ # match from start
- \s* # optional spaces
- (\d+) # line number
- \s+ # spaces
- ([A-Z][A-Z0-9_]*) # varname
- \s* # optional spaces
- , # delimiter
- \s* # optional spaces
- (-1|\d+) # instance
- \s* # optional spaces
- : # delimiter
- \s* # optional spaces
- \"( # quoted string (data)
- (?: \\\" # (of escaped dquote
- |[^\"])* # or anything not a
- )\" # literal quote)
- \s* # optional spaces
- ( # bgroup
- \# # comment leader
- \s* # optional spaces
- .* # string (comment)
- )? # egroup, optional
- $ # eol
- """,
- re.VERBOSE)
+ :param str line: CNF line
+ :rtype: {str: Any}
+ This performs the necessary decoding on values to obtain proper Python
+ strings from 8-bit encoded CNF data.
-def read_base_line(line):
+ The function only operates on individual lines. Argument strings that
+ contain data for multiple lines – this includes child lines of the current
+ CNF var! – will trigger a parsing exception.
+ """
if len(line.strip()) == 0:
return None # ignore empty lines
if line[0] == b"#":
"comment" : marshal_in_comment (comment),
}
-child_line_pattern = re.compile(b"""
- ^ # match from start
- \s* # optional spaces
- (\d+) # line number
- \s+ # spaces
- \((\d+)\) # parent
- \s+ # spaces
- ([A-Z][A-Z0-9_]*) # varname
- \s* # optional spaces
- , # delimiter
- \s* # optional spaces
- (-1|\d+) # instance
- \s* # optional spaces
- : # delimiter
- \s* # optional spaces
- \"([^\"]*)\" # quoted string (data)
- \s* # optional spaces
- ( # bgroup
- \# # comment leader
- \s* # optional spaces
- .* # string (comment)
- )? # egroup, optional
- $ # eol
- """,
- re.VERBOSE)
-
def read_child_line(line):
+ """
+ Turn one child CNF line into a dictionary.
+
+ :param str line: CNF line
+ :rtype: {str: Any}
+
+ This function only operates on individual lines. If the argument string is
+ syntactically valid but contains input representing multiple CNF vars, a
+ parse error will be thrown.
+ """
if len(line.strip()) == 0:
return None # ignore empty lines
if line[0] == "#":
}
-def parse_cnf_children(state, parent):
- lines = []
- current = get(state)
- while True:
- cnf_line = read_child_line(current)
- if cnf_line is not None:
- lines.append(cnf_line)
- state = advance(state)
- if state is None:
- break
- current = get(state)
- new_parent = get_parent(current)
- if new_parent is None:
- # drop stack
- return (state, lines, None)
- if new_parent > parent:
- # parent is further down in hierarchy -> new level
- (state, children, new_parent) = \
- parse_cnf_children (state, new_parent)
- if state is None:
- break
- cnf_line["children"] = children
- current = get(state)
- new_parent = get_parent(current)
- if new_parent is None:
- # drop stack
- return (state, lines, None)
- if new_parent < parent:
- # parent is further up in hierarchy -> pop level
- return (state, lines, new_parent)
- # new_parent == parent -> continue parsing on same level
- return (state, lines, parent)
-
-
-def parse_cnf_root(state):
- lines = []
- current = get(state)
- while state:
- cnf_line = read_base_line(current)
- if cnf_line is not None:
- lines.append(cnf_line)
- state = advance(state)
- if state is None: # -> nothing left to do
- break
- current = get(state)
- parent = get_parent(current) # peek at next line
- if parent is not None: # -> recurse into children
- (state, children, _parent) = parse_cnf_children(state, parent)
- cnf_line["children"] = children
- if state is None:
- break
- current = get(state)
- return lines
-
-
-def read_cnf(data):
- """
- Read cnf data from data bytes.
-
- :param bytes data: raw data
- :return: the parsed cnf data
- :rtype: {str, {str, str or int}}
- """
- if isinstance (data, str):
- data = to_latin1 (data)
- state = prepare(data)
- if state is None:
- raise InvalidCNF("Empty input string.")
-
- cnf = parse_cnf_root(state)
- if is_cnf(cnf) is False:
- raise TypeError("Invalid CNF_VAR.")
- return {"cnf": cnf}
-
-
-def renumber_vars(root, parent=None, toplevel=False):
- """
- Number cnfvars linearly.
+###############################################################################
+# SERIALIZATION
+###############################################################################
- If *parent* is specified, numbering will start at this offset. Also, the
- VAR *root* will be assigned this number as a parent lineno unless
- *toplevel* is set (the root var in a CNF tree obviously can’t have a
- parent).
-
- The *toplevel* parameter is useful when renumbering an existing variable
- starting at a given offset without at the same time having that offset
- assigned as a parent.
- """
- root = cnf_root (root)
- i = parent or 0
- for var in root:
- i += 1
- var["number"] = i
- if toplevel is False and parent is not None:
- var["parent"] = parent
- children = var.get("children", None)
- if children is not None:
- i = renumber_vars(children, parent=i, toplevel=False)
- return i
-
-#
-# serialization
-#
cnf_line_nest_indent = " "
cnf_line_base_fmt = "%d %s,%d: \"%s\""
cnf_line_child_fmt = "%d %s(%d) %s,%d: \"%s\""
+
def format_cnf_vars(da, var):
"""
Return a list of formatted cnf_line byte strings.
+ :param da: a tuple where the first element is the depth (0 = top-level,
+ >1 = child CNF) and the second is the string being built.
+ :type da: (int, str)
+ :param var: the CNF element to convert to string in the current iteration
+ :type var: dict
+ :returns: a tuple like `da`, where the second element should contain all
+ converted CNFs
+ :rtype: (int, str)
+
+ This function is meant to be passed to the :py:func:`functools.reduce`
+ function.
+
The variable names are uppercased unconditionally because while ``get_cnf``
is case-indifferent for variable names, ``set_cnf`` isn’t.
"""
-
depth, acc = da
line = None
if depth > 0:
% (var["number"],
cnf_line_nest_indent * depth,
var["parent"],
- var["varname"].upper (),
+ var["varname"].upper(),
var["instance"],
var["data"])
else:
line = cnf_line_base_fmt \
% (var["number"],
- var["varname"].upper (),
+ var["varname"].upper(),
var["instance"],
var["data"])
comment = var.get("comment", None)
- if comment and len (comment) != 0:
+ if comment and len(comment) != 0:
line = line + (" # %s" % comment)
- acc.append(to_latin1 (line))
+ acc.append(to_latin1(line))
children = var.get("children", None)
if children is not None:
return (depth, acc)
-CNF_FIELD_MANDATORY = set ([ "varname", "data", "instance" ])
-CNF_FIELD_OPTIONAL = set ([ "parent", "children", "comment", "number" ])
-CNF_FIELD_KNOWN = CNF_FIELD_MANDATORY | CNF_FIELD_OPTIONAL
-
-
-def is_cnf_var (obj):
- """
- Applies if all mandatory fields of a CNF_VAR and no unknown fields are
- present.
-
- .. XXX: validate field types.
- """
- assert isinstance (obj, dict)
-
- for f in CNF_FIELD_MANDATORY:
- if obj.get (f, None) is None:
- return False
-
- for f in obj:
- if f not in CNF_FIELD_KNOWN:
- return False
-
- return True
-
-
-def cnf_root (root):
+def cnf_root(root):
"""
- Of a given object, return the cnf root. This may be either a list
- containing the object itself, if it satisfies the criteria for a CNF_VAR,
- or the object contained in its toplevel field *cnf*.
+ Extract a list of CNFs from a given structure.
+
+ :param root: list of CNFs or a CNF dictionary
+ :type root: [dict] or dict
+ :raises: :py:class:`TypeError` if no CNFs can be extracted
+ :returns: list with one or more CNF objects
+ :rtype: [dict]
+
+ Output varies depending on a few conditions:
+ - If `root` is a list, return it right away
+ - If `root` is a dict corresponding to a valid CNF value, return it wrapped
+ in a list
+ - If `root` is a dict with a `cnf` key containg a list (as the JSON
+ returned by `get_cnf -j`), return the value
+ - Otherwise, raise an error
"""
- if isinstance (root, list):
+ if isinstance(root, list):
return root
if not isinstance(root, dict):
raise TypeError(
"Expected dictionary of CNF_VARs, got %s." % type(root))
- if is_cnf_var (root):
- return [ root ]
+ if is_cnf_var(root):
+ return [root]
cnf = root.get("cnf", None)
if not isinstance(cnf, list):
raise TypeError("Expected list of CNF_VARs, got %s." % type(cnf))
return cnf
-def normalize_cnf (cnf):
+def normalize_cnf(cnf):
"""
Ensure the output conforms to set_cnf()’s expectations.
+
+ :param cnf: list of CNF objects to normalize
+ :type cnf: [dict]
+ :returns: normalized list
+ :rtype: [list]
"""
- if isinstance (cnf, list) is False:
- raise MalformedCNF ("expected list of CNF_VARs, got [%s]" % type (cnf))
- def norm (var):
+ if isinstance(cnf, list) is False:
+ raise MalformedCNF("expected list of CNF_VARs, got [%s]" % type(cnf))
+ def norm(var):
vvar = \
{ "number" : var ["number"]
- , "varname" : var ["varname"].upper ()
+ , "varname" : var ["varname"].upper()
, "instance" : var ["instance"]
, "data" : var ["data"]
}
- children = var.get ("children", None)
+ children = var.get("children", None)
if children is not None:
- vvar ["children"] = normalize_cnf (children)
+ vvar ["children"] = normalize_cnf(children)
+
+ parent = var.get("parent", None)
+ if parent is not None:
+ vvar ["parent"] = var["parent"]
+
+ comment = var.get("comment", None)
+ if comment is not None:
+ vvar ["comment"] = var["comment"]
return vvar
- return [ norm (var) for var in cnf ]
+ return [norm(var) for var in cnf]
-def print_cnf_raw(root, out=None):
- """`if root is not None: out.write(root)`."""
- if root is not None:
- out.write(root)
+###############################################################################
+# TRAVERSAL
+###############################################################################
-def write_cnf_raw(*argv, **kw_argv):
- """Alias for :py:func:`print_cnf_raw`."""
- print_cnf_raw(*argv, **kw_argv)
+def walk_cnf(cnf, nested, fun, acc):
+ """
+ Depth-first traversal of a CNF tree.
+
+ :type cnf: cnf list
+ :type nested: bool
+ :type fun: 'a -> bool -> (cnf stuff) -> 'a
+ :type acc: 'a
+ :rtype: 'a
+
+ Executes ``fun`` recursively for each node in the tree. The function
+ receives the accumulator ``acc`` which can be of an arbitrary type as first
+ argument. The second argument is a flag indicating whether the current
+ CNF var is a child (if ``True``) or a parent var. CNF member fields are
+ passed via named optional arguments.
+ """
+ for var in cnf:
+ acc = fun(acc,
+ nested,
+ comment=var.get("comment", None),
+ data=var.get("data", None),
+ instance=var.get("instance", None),
+ number=var.get("number", None),
+ parent=var.get("parent", None),
+ varname=var.get("varname", None))
+ children = var.get("children", None)
+ if children is not None:
+ acc = walk_cnf(children, True, fun, acc)
+ return acc
+
+
+def renumber_vars(root, parent=None, toplevel=False):
+ """
+ Number cnfvars linearly.
+
+ If *parent* is specified, numbering will start at this offset. Also, the
+ VAR *root* will be assigned this number as a parent lineno unless
+ *toplevel* is set (the root var in a CNF tree obviously can’t have a
+ parent).
+
+ The *toplevel* parameter is useful when renumbering an existing variable
+ starting at a given offset without at the same time having that offset
+ assigned as a parent.
+ """
+ root = cnf_root (root)
+ i = parent or 0
+ for var in root:
+ i += 1
+ var["number"] = i
+ if toplevel is False and parent is not None:
+ var["parent"] = parent
+ children = var.get("children", None)
+ if children is not None:
+ i = renumber_vars(children, parent=i, toplevel=False)
+ return i
+
+
+def count_vars(root):
+ """
+ Traverse the cnf structure recursively, counting VAR objects (CNF lines).
+ """
+ cnf = cnf_root(root)
+ if cnf is None:
+ raise InvalidCNF(root)
+ return walk_cnf(cnf, True, lambda n, _nested, **_kwa: n + 1, 0)
+
+#
+# querying
+#
+
+
+def get_vars(cnf, data=None, instance=None):
+ """
+ get_vars -- Query a CNF_VAR structure. Skims the *toplevel* of the CNF_VAR
+ structure for entries with a matching `data` or `instance` field.
+
+ :type cnf: CNF_VAR
+ :type data: str
+ :type instance: int
+ :rtype: CNF_VAR
+ :returns: The structure containing only references to the
+ matching variables. Containing an empty list of
+ variables in case there is no match.
+
+ Values are compared literally. If both ``instance`` and ``data`` are
+ specified, vars will be compared against both.
+ """
+ cnf = cnf["cnf"]
+ if cnf:
+ criterion = lambda _var: False
+ if data:
+ if instance:
+ criterion = lambda var: var[
+ "data"] == data and var["instance"] == instance
+ else:
+ criterion = lambda var: var["data"] == data
+ elif instance:
+ criterion = lambda var: var["instance"] == instance
+
+ return {"cnf": [var for var in cnf if criterion(var) is True]}
+
+ return {"cnf": []}
+
+
+###############################################################################
+# PRINTING/DUMPING
+###############################################################################
+#
+# Print/dump raw CNF values
+#
+
def output_cnf(root, out, renumber=False):
"""
Dump a textual representation of given CNF VAR structure to given stream.
:type root: dict or list or anything that :py:func:`cnf_root` accepts
:param out: file-like object or something with a `write(str)` function
:param bool renumber: Whether to renumber cnfvars first
+
+ Files are converted to the 8-bit format expected by CNF so they can be fed
+ directly into libcnffile.
"""
cnf = cnf_root(root)
if renumber is True:
print_cnf(*argv, **kw_argv)
+def print_cnf_raw(root, out=None):
+ """`if root is not None: out.write(root)`."""
+ if root is not None:
+ out.write(root)
+
+
+def write_cnf_raw(*argv, **kw_argv):
+ """Alias for :py:func:`print_cnf_raw`."""
+ print_cnf_raw(*argv, **kw_argv)
+
+
+#
+# Print/dump CNF values in JSON format
+#
+
+
def output_json(root, out, renumber=False):
"""
Dump CNF_VAR structure to file-like object in json format.
"""Alias for :py:func:`print_cnf_json`."""
print_cnf_json(*argv, **kw_argv)
-#
-# querying
-#
-def get_vars(cnf, data=None, instance=None):
- """
- get_vars -- Query a CNF_VAR structure. Skims the *toplevel* of the CNF_VAR
- structure for entries with a matching `data` or `instance` field.
+###############################################################################
+# ENTRY POINT FOR DEVELOPMENT
+###############################################################################
- :type cnf: CNF_VAR
- :type data: str
- :type instance: int
- :rtype: CNF_VAR
- :returns: The structure containing only references to the
- matching variables. Containing an empty list of
- variables in case there is no match.
- """
- cnf = cnf["cnf"]
- if cnf:
- criterion = lambda _var: False
- if data:
- if instance:
- criterion = lambda var: var[
- "data"] == data and var["instance"] == instance
- else:
- criterion = lambda var: var["data"] == data
- elif instance:
- criterion = lambda var: var["instance"] == instance
- return {"cnf": [var for var in cnf if criterion(var) is True]}
-
- return {"cnf": []}
-
-def usage ():
- print ("usage: cnfvar.py -" , file=sys.stderr)
- print ("" , file=sys.stderr)
- print (" Read CNF from stdin.", file=sys.stderr)
- print ("" , file=sys.stderr)
-
-#
-# entry point for development
-#
+def usage():
+ print("usage: cnfvar.py -" , file=sys.stderr)
+ print("" , file=sys.stderr)
+ print(" Read CNF from stdin.", file=sys.stderr)
+ print("" , file=sys.stderr)
def main(argv):
cnff = get_vars(cnf, instance=2, data="FAX")
print_cnf(cnff)
return 0
- usage ()
+ usage()
return -1
if __name__ == "__main__":
- sys.exit (main(sys.argv))
+ sys.exit(main(sys.argv))
------------------------------------------------------
Read / write / merge guest cnf var sets, even on host.
+DEPRECATED! Please do not extend this or add new uses of this module, use
+:py:mod:`pyi2ncommon.arnied_api` or :py:mod:`pyi2ncommon.cnfvar` instead!
+
Copyright: Intra2net AG
log = logging.getLogger('pyi2ncommon.simple_cnf')
from . import arnied_wrapper
-from . import cnfvar
+from . import cnfvar_old
from . import sysmisc
###############################################################################
new_dict = child.get_single_dict()
new_dict['parent'] = number
new_children.append(new_dict)
- cnfvar.renumber_vars({'cnf':new_children}, number)
+ cnfvar_old.renumber_vars({'cnf':new_children}, number)
children = new_children
elif number is None:
number = self._find_new_number(self.__cnfvars)
def append_file(self, cnf, replacements=None):
"""Append conf var data from file."""
- return self.append_file_generic(cnfvar.read_cnf, cnf,
+ return self.append_file_generic(cnfvar_old.read_cnf, cnf,
replacements=replacements)
def append_file_json(self, cnf, replacements=None):
"""Append conf var data from json file."""
- return self.append_file_generic(cnfvar.read_cnf_json, cnf,
+ return self.append_file_generic(cnfvar_old.read_cnf_json, cnf,
replacements=replacements)
def append_guest_vars(self, vm=None, varname=None, replacements=None):
filename = arnied_wrapper.generate_config_path(dumped=True)
with open(filename, 'w') as out:
- cnfvar.output_json({"cnf": current}, out, renumber=True)
+ cnfvar_old.output_json({"cnf": current}, out, renumber=True)
return filename
current = self.__cnfvars
if renumber:
log.info("enforce consistent CNF_LINE numbering")
- cnfvar.renumber_vars(current)
+ cnfvar_old.renumber_vars(current)
log.info("inject configuration %s" % "into guest" if vm else "in place")
arnied_wrapper.set_cnf_dynamic({"cnf": current},
config_file=gen_tmpname(), vm=vm)
:returns: config in json format
:rtype: str
"""
- return cnfvar.dump_json_string({"cnf": self.__cnfvars}, renumber=True)
+ return cnfvar_old.dump_json_string({"cnf": self.__cnfvars}, renumber=True)
def pretty_print(self, print_func=None):
"""
""" like the builtin print function but also accepts color args
If any arg of :py:func:`colored` is given in `kwargs`, will run text with
- color-args through that function. Runs built-in :py:builtin:`print`
+ color-args through that function. Runs built-in :py:func:`print`
function with result and other args.
...todo:: color all args, not just first
--- /dev/null
+1 ACME_DEBUG_ENABLE,0: "0"
+2 ACME_KEY_RENEWAL_DAYS,0: "30"
+3 ADTASKS_CRON,0: "0123456"
+4 (3) ADTASKS_CRON_BEGIN,0: "0"
+5 (3) ADTASKS_CRON_END,0: "86400"
+6 (3) ADTASKS_CRON_EVERY,0: "3600"
+7 AD_ENABLE_UI,0: "0"
+8 AD_LDAP_BIND_ACCOUNT,0: ""
+9 AD_LDAP_BIND_PASSWORD,0: ""
+10 AD_MACHINE,0: ""
+11 AD_MACHINE_CHANGEPW_INTERVAL,0: "30"
+12 AD_MACHINE_CREDENTIALS,0: ""
+13 BACKUP_ACCESS_RESTRICTED_ONLY,0: "1"
+14 BACKUP_BACKEND,0: "NEW"
+15 BACKUP_COMPRESS_ENABLE,0: "1"
+16 BACKUP_CRON,1: "6"
+17 (16) BACKUP_CRON_BEGIN,0: "79200"
+18 BACKUP_CRON,2: "123456"
+19 (18) BACKUP_CRON_BEGIN,0: "23400"
+20 (18) BACKUP_CRON_IS_DIFF_BACKUP,0: "1"
+21 BACKUP_CRON,3: "123456"
+22 (21) BACKUP_CRON_BEGIN,0: "45000"
+23 (21) BACKUP_CRON_IS_DIFF_BACKUP,0: "1"
+24 BACKUP_CRON,4: "0123456"
+25 (24) BACKUP_CRON_BEGIN,0: "68400"
+26 (24) BACKUP_CRON_IS_DIFF_BACKUP,0: "1"
+27 BACKUP_DAYS_TO_KEEP,0: "3"
+28 BACKUP_ENCRYPT_ENABLE,0: "0"
+29 BACKUP_ENCRYPT_PASSWORD,0: ""
+30 BACKUP_ENCRYPT_SECRET_HASH,0: ""
+31 BACKUP_PUSH_DAYS_TO_KEEP,0: "3"
+32 BACKUP_PUSH_SERVERTYPE,0: "SMB"
+33 BACKUP_PUSH_SETS_TO_KEEP,0: "1"
+34 BACKUP_RESTRICT_AUTH_ENABLE,0: "1"
+35 BACKUP_RESTRICT_IP_ENABLE,0: "0"
+36 BACKUP_RESTRICT_IP_REF,0: "0"
+37 BACKUP_RESTRICT_LOGIN,0: "backup"
+38 BACKUP_RESTRICT_PASSWORD,0: "kadkiovop0"
+39 BACKUP_SETS_TO_KEEP,0: "2"
+40 BACKUP_VOLUME_SIZE,0: "649"
+41 CNFCLEAN_CRON,0: "0123456"
+42 (41) CNFCLEAN_CRON_BEGIN,0: "0"
+43 (41) CNFCLEAN_CRON_END,0: "86400"
+44 (41) CNFCLEAN_CRON_EVERY,0: "3600"
+45 DATA_PRIVACY_ATTACHMENT_QUARANTINE_ACCESS,0: "FULL"
+46 DATA_PRIVACY_SPAM_QUARANTINE_ACCESS,0: "PROTECTED"
+47 DHCP_DNS_SERVER,0: ""
+48 DHCP_DWARDEN_ENABLE,0: "1"
+49 DHCP_ENABLE,0: "0"
+50 DHCP_EXCLUDE_NIC_REF,0: "0"
+51 DHCP_EXCLUDE_NIC_REF,2: "2"
+52 DHCP_EXCLUDE_NIC_REF,3: "3"
+53 DHCP_GATEWAY_IP,0: ""
+54 DHCP_LEASE_TIME_SEC,0: "86400"
+55 DHCP_WINS_SERVER,0: ""
+56 DIALOUT_TIMESERVER,0: "0.intra2net.pool.ntp.org"
+57 DIALOUT_TIMESERVER,1: "1.intra2net.pool.ntp.org"
+58 DIALOUT_TIMESERVER,2: "2.intra2net.pool.ntp.org"
+59 DISPLAY_RESTART_CRON,0: "0123456"
+60 (59) DISPLAY_RESTART_CRON_BEGIN,0: "1620"
+61 (59) DISPLAY_RESTART_CRON_END,0: "84780"
+62 (59) DISPLAY_RESTART_CRON_EVERY,0: "3600"
+63 DNS_REBIND_PREVENTION,0: "1"
+64 EMAILFILTER_BAN_ACTION,0: "REPLACE"
+65 EMAILFILTER_BAN_DEFAULT_FILTERLIST_REF,0: "2"
+66 EMAILFILTER_BAN_DELETEDAYS,0: "60"
+67 EMAILFILTER_BAN_FILTERLIST,0: "Vordefiniert: Alles erlaubt"
+68 (67) EMAILFILTER_BAN_FILTERLIST_ENCRYPTED,0: "ALLOW"
+69 (67) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS,0: ""
+70 (67) EMAILFILTER_BAN_FILTERLIST_FILTER_OFFICE_FILES,0: "BY_FILTERLIST"
+71 (67) EMAILFILTER_BAN_FILTERLIST_MIMETYPES,0: ""
+72 (67) EMAILFILTER_BAN_FILTERLIST_MODE,0: "BLOCK"
+73 (67) EMAILFILTER_BAN_FILTERLIST_PREDEFINED_ID,0: "0"
+74 EMAILFILTER_BAN_FILTERLIST,1: "Vordefiniert: Alles verboten"
+75 (74) EMAILFILTER_BAN_FILTERLIST_ENCRYPTED,0: "BLOCK"
+76 (74) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS,0: ""
+77 (74) EMAILFILTER_BAN_FILTERLIST_FILTER_OFFICE_FILES,0: "BY_FILTERLIST"
+78 (74) EMAILFILTER_BAN_FILTERLIST_MIMETYPES,0: ""
+79 (78) EMAILFILTER_BAN_FILTERLIST_MIMETYPES_NAME,0: "text/plain"
+80 (78) EMAILFILTER_BAN_FILTERLIST_MIMETYPES_NAME,1: "text/html"
+81 (74) EMAILFILTER_BAN_FILTERLIST_MODE,0: "ALLOW"
+82 (74) EMAILFILTER_BAN_FILTERLIST_PREDEFINED_ID,0: "1"
+83 EMAILFILTER_BAN_FILTERLIST,2: "Vordefiniert: Ausführbare Dateien"
+84 (83) EMAILFILTER_BAN_FILTERLIST_ENCRYPTED,0: "BLOCK"
+85 (83) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS,0: ""
+86 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,0: "ace"
+87 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,1: "ade"
+88 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,2: "adp"
+89 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,3: "ani"
+90 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,4: "application"
+91 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,5: "arc"
+92 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,6: "arj"
+93 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,7: "bas"
+94 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,8: "bat"
+95 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,9: "cdxml"
+96 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,10: "chm"
+97 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,11: "cmd"
+98 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,12: "com"
+99 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,13: "cpl"
+100 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,14: "crt"
+101 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,15: "cur"
+102 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,16: "dll"
+103 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,17: "exe"
+104 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,18: "gadget"
+105 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,19: "grp"
+106 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,20: "hlp"
+107 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,21: "hta"
+108 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,22: "ico"
+109 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,23: "inf"
+110 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,24: "ins"
+111 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,25: "iso"
+112 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,26: "isp"
+113 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,27: "jar"
+114 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,28: "js"
+115 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,29: "jse"
+116 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,30: "lha"
+117 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,31: "lnk"
+118 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,32: "mdb"
+119 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,33: "mde"
+120 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,34: "msc"
+121 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,35: "msh"
+122 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,36: "msh1"
+123 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,37: "msh1xml"
+124 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,38: "msh2"
+125 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,39: "msh2xml"
+126 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,40: "mshxml"
+127 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,41: "msi"
+128 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,42: "msp"
+129 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,43: "mst"
+130 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,44: "ocx"
+131 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,45: "pcd"
+132 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,46: "pif"
+133 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,47: "ps1"
+134 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,48: "ps1xml"
+135 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,49: "ps2"
+136 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,50: "ps2xml"
+137 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,51: "psc1"
+138 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,52: "psc2"
+139 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,53: "psd1"
+140 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,54: "psm1"
+141 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,55: "pssc"
+142 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,56: "reg"
+143 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,57: "scf"
+144 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,58: "scr"
+145 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,59: "sct"
+146 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,60: "shb"
+147 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,61: "shs"
+148 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,62: "swf"
+149 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,63: "url"
+150 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,64: "vb"
+151 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,65: "vbe"
+152 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,66: "vbs"
+153 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,67: "ws"
+154 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,68: "wsc"
+155 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,69: "wsf"
+156 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,70: "wsh"
+157 (85) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS_NAME,71: "zoo"
+158 (83) EMAILFILTER_BAN_FILTERLIST_FILTER_OFFICE_FILES,0: "SUSPICIOUS_MACROS"
+159 (83) EMAILFILTER_BAN_FILTERLIST_MIMETYPES,0: ""
+160 (159) EMAILFILTER_BAN_FILTERLIST_MIMETYPES_NAME,0: "application/x-msdos-program"
+161 (159) EMAILFILTER_BAN_FILTERLIST_MIMETYPES_NAME,1: "application/x-msdownload"
+162 (83) EMAILFILTER_BAN_FILTERLIST_MODE,0: "BLOCK"
+163 (83) EMAILFILTER_BAN_FILTERLIST_PREDEFINED_ID,0: "2"
+164 EMAILFILTER_BAN_INCOMING,0: "1"
+165 EMAILFILTER_BAN_NOTIFY_ADMIN,0: "1"
+166 EMAILFILTER_BAN_NOTIFY_ADMIN_REF,0: "-1"
+167 EMAILFILTER_BAN_NOTIFY_LOCAL_SENDER,0: "0"
+168 EMAILFILTER_BAN_OUTGOING,0: "0"
+169 EMAIL_AUTH_ONLY_CONNECTION_ENCRYPTED,0: "1"
+170 EMAIL_BLOCK_NEW,0: "0"
+171 EMAIL_DELAYWARNING_HOURS,0: "24"
+172 EMAIL_FORWARD_USE_SERVER_NAME,0: "0"
+173 EMAIL_ILLEGAL_CHAR_HANDLING,0: "STRIP"
+174 EMAIL_MAX_INET_PARALLEL_SEND,0: "5"
+175 EMAIL_MAX_QUEUE_LIFETIME,0: "120"
+176 EMAIL_MAX_RECIPIENTS,0: "100"
+177 EMAIL_MAX_SIZE,0: "100"
+178 EMAIL_POSTMASTER_NOTIFY_BOUNCE,0: "0"
+179 EMAIL_POSTMASTER_REF,0: "1"
+180 EMAIL_RELAY,0: "Standard"
+181 (180) EMAIL_RELAY_AUTH_LOGIN,0: ""
+182 (180) EMAIL_RELAY_AUTH_MODE,0: "none"
+183 (180) EMAIL_RELAY_AUTH_PASSWORD,0: ""
+184 (180) EMAIL_RELAY_MODE,0: "direct"
+185 (180) EMAIL_RELAY_SERVER,0: ""
+186 (180) EMAIL_RELAY_SERVER_PORT,0: "25"
+187 (180) EMAIL_RELAY_TYPE,0: "DEFAULT"
+188 EMAIL_RELAY_DEFAULT_REF,0: "0"
+189 EMAIL_TIMEOUT_FETCHMAIL,0: "90"
+190 EMAIL_TLS_RECEIVE,0: "1"
+191 EMAIL_TLS_SEND,0: "1"
+192 EMAIL_TRANSFER_DISABLED,0: "0"
+193 FAX_IDENTIFICATION_NAME,0: "Fax"
+194 FAX_IDENTIFICATION_NUMBER,0: ""
+195 FAX_RECEIVE_ENABLE,0: "0"
+196 FAX_SEND_ENABLE,0: "0"
+197 FAX_SEND_MSN_REF,0: "-1"
+198 FIREWALL_BLOCK_PASSWORD_GUESSING,0: "1"
+199 FIREWALL_CHECK_LOCAL_SPOOFING,0: "1"
+200 FIREWALL_CHECK_MACADDR,0: "1"
+201 FIREWALL_NETGROUP,99: "QA host IP"
+202 (201) FIREWALL_NETGROUP_NETWORK,0: ""
+203 (202) FIREWALL_NETGROUP_NETWORK_IP,0: "192.168.1.254"
+204 (202) FIREWALL_NETGROUP_NETWORK_NETMASK,0: "255.255.255.255"
+205 FIREWALL_RULESET,1: "Basis LAN und lokale Netze"
+206 (205) FIREWALL_RULESET_AUTOMATIC_ANSWER_RULE,0: "1"
+207 (205) FIREWALL_RULESET_PREDEFINED_ID,0: "1"
+208 (205) FIREWALL_RULESET_PROFILE_TYPE,0: "FULL"
+209 (205) FIREWALL_RULESET_RULE,1: ""
+210 (209) FIREWALL_RULESET_RULE_ACTION,0: "ACCEPT"
+211 (209) FIREWALL_RULESET_RULE_DST_MAGIC,0: "0"
+212 (209) FIREWALL_RULESET_RULE_LIMIT_FOR_ACTION_ENABLE,0: "0"
+213 (209) FIREWALL_RULESET_RULE_LIMIT_FOR_LOG_ENABLE,0: "0"
+214 (209) FIREWALL_RULESET_RULE_LIMIT_PACKETS_AVERAGE_COUNT,0: ""
+215 (209) FIREWALL_RULESET_RULE_LIMIT_PACKETS_PEAK_COUNT,0: ""
+216 (209) FIREWALL_RULESET_RULE_LOG_ENABLE,0: "0"
+217 (209) FIREWALL_RULESET_RULE_LOG_MESSAGE,0: ""
+218 (209) FIREWALL_RULESET_RULE_SERVICE_INCLUDE_SERVICEGROUP_REF,0: "1"
+219 (209) FIREWALL_RULESET_RULE_SERVICE_INCLUDE_SERVICEGROUP_REF,1: "2"
+220 (209) FIREWALL_RULESET_RULE_SERVICE_INCLUDE_SERVICEGROUP_REF,2: "22"
+221 (209) FIREWALL_RULESET_RULE_SERVICE_INCLUDE_SERVICEGROUP_REF,3: "39"
+222 (209) FIREWALL_RULESET_RULE_SERVICE_INCLUDE_SERVICEGROUP_REF,4: "6"
+223 (209) FIREWALL_RULESET_RULE_SERVICE_INCLUDE_SERVICEGROUP_REF,5: "9"
+224 (209) FIREWALL_RULESET_RULE_SERVICE_INCLUDE_SERVICEGROUP_REF,6: "24"
+225 (205) FIREWALL_RULESET_RULE,2: "Zugriff auf alle lokalen Netze"
+226 (225) FIREWALL_RULESET_RULE_ACTION,0: "ACCEPT"
+227 (225) FIREWALL_RULESET_RULE_DST_MAGIC,0: "0"
+228 (227) FIREWALL_RULESET_RULE_DST_MAGIC_IS_NOT,0: "1"
+229 (225) FIREWALL_RULESET_RULE_DST_MAGIC,1: "1"
+230 (225) FIREWALL_RULESET_RULE_LIMIT_FOR_ACTION_ENABLE,0: "0"
+231 (225) FIREWALL_RULESET_RULE_LIMIT_FOR_LOG_ENABLE,0: "0"
+232 (225) FIREWALL_RULESET_RULE_LIMIT_PACKETS_AVERAGE_COUNT,0: ""
+233 (225) FIREWALL_RULESET_RULE_LIMIT_PACKETS_PEAK_COUNT,0: ""
+234 (225) FIREWALL_RULESET_RULE_LOG_ENABLE,0: "0"
+235 (225) FIREWALL_RULESET_RULE_LOG_MESSAGE,0: ""
+236 (205) FIREWALL_RULESET_RULE,3: ""
+237 (236) FIREWALL_RULESET_RULE_ACTION,0: "REJECT"
+238 (236) FIREWALL_RULESET_RULE_CHECK_TCP_FLAGS,0: "DISABLED"
+239 (236) FIREWALL_RULESET_RULE_LIMIT_FOR_ACTION_ENABLE,0: "0"
+240 (236) FIREWALL_RULESET_RULE_LIMIT_FOR_LOG_ENABLE,0: "1"
+241 (236) FIREWALL_RULESET_RULE_LIMIT_PACKETS_AVERAGE_COUNT,0: "5"
+242 (236) FIREWALL_RULESET_RULE_LIMIT_PACKETS_AVERAGE_PERIOD,0: "SEC"
+243 (236) FIREWALL_RULESET_RULE_LIMIT_PACKETS_PEAK_COUNT,0: "5"
+244 (236) FIREWALL_RULESET_RULE_LOG_ENABLE,0: "1"
+245 (236) FIREWALL_RULESET_RULE_LOG_MESSAGE,0: "REJECT local"
+246 (236) FIREWALL_RULESET_RULE_TIME_INCLUDE_TIME_REF,0: "-1"
+247 (205) FIREWALL_RULESET_USAGE,0: "LANVPN"
+248 FIREWALL_RULESET,2: "Nur E-Mail"
+249 (248) FIREWALL_RULESET_CLIENT_ACCESSRIGHT,0: "sl_mail"
+250 (248) FIREWALL_RULESET_CLIENT_ACCESS_ALL_NETWORKS,0: "1"
+251 (248) FIREWALL_RULESET_CLIENT_FAX_SEND_ALLOWED,0: "1"
+252 (248) FIREWALL_RULESET_CLIENT_MAIL_LOCAL_ONLY,0: "1"
+253 (248) FIREWALL_RULESET_CLIENT_PROXY_MODE,0: "blocked"
+254 (248) FIREWALL_RULESET_PREDEFINED_ID,0: "2"
+255 (248) FIREWALL_RULESET_PROFILE_TYPE,0: "SIMPLE_CLIENT"
+256 FIREWALL_RULESET,3: "WWW, FTP, E-Mail"
+257 (256) FIREWALL_RULESET_CLIENT_ACCESSRIGHT,0: "sl_web"
+258 (256) FIREWALL_RULESET_CLIENT_ACCESS_ALL_NETWORKS,0: "1"
+259 (256) FIREWALL_RULESET_CLIENT_FAX_SEND_ALLOWED,0: "1"
+260 (256) FIREWALL_RULESET_CLIENT_MAIL_LOCAL_ONLY,0: "1"
+261 (256) FIREWALL_RULESET_CLIENT_PROXY_MODE,0: "allowed"
+262 (256) FIREWALL_RULESET_PREDEFINED_ID,0: "3"
+263 (256) FIREWALL_RULESET_PROFILE_TYPE,0: "SIMPLE_CLIENT"
+264 FIREWALL_RULESET,4: "WWW, FTP, E-Mail + Transp. Proxy"
+265 (264) FIREWALL_RULESET_CLIENT_ACCESSRIGHT,0: "sl_web"
+266 (264) FIREWALL_RULESET_CLIENT_ACCESS_ALL_NETWORKS,0: "1"
+267 (264) FIREWALL_RULESET_CLIENT_FAX_SEND_ALLOWED,0: "1"
+268 (264) FIREWALL_RULESET_CLIENT_MAIL_LOCAL_ONLY,0: "1"
+269 (264) FIREWALL_RULESET_CLIENT_PROXY_MODE,0: "trans"
+270 (264) FIREWALL_RULESET_PREDEFINED_ID,0: "4"
+271 (264) FIREWALL_RULESET_PROFILE_TYPE,0: "SIMPLE_CLIENT"
+272 FIREWALL_RULESET,5: "Vollzugriff"
+273 (272) FIREWALL_RULESET_CLIENT_ACCESSRIGHT,0: "sl_all"
+274 (272) FIREWALL_RULESET_CLIENT_ACCESS_ALL_NETWORKS,0: "1"
+275 (272) FIREWALL_RULESET_CLIENT_FAX_SEND_ALLOWED,0: "1"
+276 (272) FIREWALL_RULESET_CLIENT_MAIL_LOCAL_ONLY,0: "0"
+277 (272) FIREWALL_RULESET_CLIENT_PROXY_MODE,0: "allowed"
+278 (272) FIREWALL_RULESET_PREDEFINED_ID,0: "5"
+279 (272) FIREWALL_RULESET_PROFILE_TYPE,0: "SIMPLE_CLIENT"
+280 FIREWALL_RULESET,6: "Basis Provider"
+281 (280) FIREWALL_RULESET_PREDEFINED_ID,0: "6"
+282 (280) FIREWALL_RULESET_PROFILE_TYPE,0: "SIMPLE_PROVIDER"
+283 (280) FIREWALL_RULESET_PROVIDER_HTTPS_OPEN,0: "0"
+284 (280) FIREWALL_RULESET_PROVIDER_HTTP_OPEN,0: "0"
+285 (280) FIREWALL_RULESET_PROVIDER_POP3SIMAPS_OPEN,0: "0"
+286 (280) FIREWALL_RULESET_PROVIDER_PORT_FORWARDING_ENABLE,0: "1"
+287 (280) FIREWALL_RULESET_PROVIDER_SMTP_OPEN,0: "0"
+288 (280) FIREWALL_RULESET_PROVIDER_VPN_OPEN,0: "0"
+289 FIREWALL_RULESET,7: "HTTPS, Let's Encrypt + VPN"
+290 (289) FIREWALL_RULESET_PREDEFINED_ID,0: "7"
+291 (289) FIREWALL_RULESET_PROFILE_TYPE,0: "SIMPLE_PROVIDER"
+292 (289) FIREWALL_RULESET_PROVIDER_HTTPS_OPEN,0: "1"
+293 (289) FIREWALL_RULESET_PROVIDER_HTTP_OPEN,0: "1"
+294 (289) FIREWALL_RULESET_PROVIDER_POP3SIMAPS_OPEN,0: "0"
+295 (289) FIREWALL_RULESET_PROVIDER_PORT_FORWARDING_ENABLE,0: "1"
+296 (289) FIREWALL_RULESET_PROVIDER_SMTP_OPEN,0: "0"
+297 (289) FIREWALL_RULESET_PROVIDER_VPN_OPEN,0: "1"
+298 FIREWALL_RULESET,8: "Nur Internet"
+299 (298) FIREWALL_RULESET_PREDEFINED_ID,0: "8"
+300 (298) FIREWALL_RULESET_PROFILE_TYPE,0: "FULL"
+301 (298) FIREWALL_RULESET_RULE,1: ""
+302 (301) FIREWALL_RULESET_RULE_ACTION,0: "ACCEPT"
+303 (301) FIREWALL_RULESET_RULE_DST_MAGIC,0: "8"
+304 (301) FIREWALL_RULESET_RULE_LIMIT_FOR_ACTION_ENABLE,0: "0"
+305 (301) FIREWALL_RULESET_RULE_LIMIT_FOR_LOG_ENABLE,0: "0"
+306 (301) FIREWALL_RULESET_RULE_LIMIT_PACKETS_AVERAGE_COUNT,0: ""
+307 (301) FIREWALL_RULESET_RULE_LIMIT_PACKETS_PEAK_COUNT,0: ""
+308 (301) FIREWALL_RULESET_RULE_LOG_ENABLE,0: "0"
+309 (301) FIREWALL_RULESET_RULE_LOG_MESSAGE,0: ""
+310 (298) FIREWALL_RULESET_USAGE,0: "SOCKS"
+311 FIREWALL_RULESET,9: "Basis LAN"
+312 (311) FIREWALL_RULESET_AUTOMATIC_ANSWER_RULE,0: "1"
+313 (311) FIREWALL_RULESET_PREDEFINED_ID,0: "9"
+314 (311) FIREWALL_RULESET_PROFILE_TYPE,0: "FULL"
+315 (311) FIREWALL_RULESET_RULE,1: ""
+316 (315) FIREWALL_RULESET_RULE_ACTION,0: "ACCEPT"
+317 (315) FIREWALL_RULESET_RULE_DST_MAGIC,0: "0"
+318 (315) FIREWALL_RULESET_RULE_LIMIT_FOR_ACTION_ENABLE,0: "0"
+319 (315) FIREWALL_RULESET_RULE_LIMIT_FOR_LOG_ENABLE,0: "0"
+320 (315) FIREWALL_RULESET_RULE_LIMIT_PACKETS_AVERAGE_COUNT,0: ""
+321 (315) FIREWALL_RULESET_RULE_LIMIT_PACKETS_PEAK_COUNT,0: ""
+322 (315) FIREWALL_RULESET_RULE_LOG_ENABLE,0: "0"
+323 (315) FIREWALL_RULESET_RULE_LOG_MESSAGE,0: ""
+324 (315) FIREWALL_RULESET_RULE_SERVICE_INCLUDE_SERVICEGROUP_REF,0: "1"
+325 (315) FIREWALL_RULESET_RULE_SERVICE_INCLUDE_SERVICEGROUP_REF,1: "2"
+326 (315) FIREWALL_RULESET_RULE_SERVICE_INCLUDE_SERVICEGROUP_REF,2: "22"
+327 (315) FIREWALL_RULESET_RULE_SERVICE_INCLUDE_SERVICEGROUP_REF,3: "39"
+328 (315) FIREWALL_RULESET_RULE_SERVICE_INCLUDE_SERVICEGROUP_REF,4: "6"
+329 (315) FIREWALL_RULESET_RULE_SERVICE_INCLUDE_SERVICEGROUP_REF,5: "9"
+330 (315) FIREWALL_RULESET_RULE_SERVICE_INCLUDE_SERVICEGROUP_REF,6: "24"
+331 (311) FIREWALL_RULESET_RULE,2: ""
+332 (331) FIREWALL_RULESET_RULE_ACTION,0: "REJECT"
+333 (331) FIREWALL_RULESET_RULE_CHECK_TCP_FLAGS,0: "DISABLED"
+334 (331) FIREWALL_RULESET_RULE_LIMIT_FOR_ACTION_ENABLE,0: "0"
+335 (331) FIREWALL_RULESET_RULE_LIMIT_FOR_LOG_ENABLE,0: "1"
+336 (331) FIREWALL_RULESET_RULE_LIMIT_PACKETS_AVERAGE_COUNT,0: "5"
+337 (331) FIREWALL_RULESET_RULE_LIMIT_PACKETS_AVERAGE_PERIOD,0: "SEC"
+338 (331) FIREWALL_RULESET_RULE_LIMIT_PACKETS_PEAK_COUNT,0: "5"
+339 (331) FIREWALL_RULESET_RULE_LOG_ENABLE,0: "1"
+340 (331) FIREWALL_RULESET_RULE_LOG_MESSAGE,0: "REJECT local"
+341 (331) FIREWALL_RULESET_RULE_TIME_INCLUDE_TIME_REF,0: "-1"
+342 (311) FIREWALL_RULESET_USAGE,0: "LANVPN"
+343 FIREWALL_RULESET,99: "Pyro4 + Basis-LAN"
+344 (343) FIREWALL_RULESET_AUTOMATIC_ANSWER_RULE,0: "1"
+345 (343) FIREWALL_RULESET_PROFILE_TYPE,0: "FULL"
+346 (343) FIREWALL_RULESET_RULE,1: "Pyro4 access"
+347 (346) FIREWALL_RULESET_RULE_ACTION,0: "ACCEPT"
+348 (346) FIREWALL_RULESET_RULE_DST_MAGIC,0: "0"
+349 (346) FIREWALL_RULESET_RULE_LIMIT_FOR_ACTION_ENABLE,0: "0"
+350 (346) FIREWALL_RULESET_RULE_LIMIT_FOR_LOG_ENABLE,0: "0"
+351 (346) FIREWALL_RULESET_RULE_LIMIT_PACKETS_AVERAGE_COUNT,0: ""
+352 (346) FIREWALL_RULESET_RULE_LIMIT_PACKETS_PEAK_COUNT,0: ""
+353 (346) FIREWALL_RULESET_RULE_LOG_ENABLE,0: "0"
+354 (346) FIREWALL_RULESET_RULE_LOG_MESSAGE,0: ""
+355 (346) FIREWALL_RULESET_RULE_SRC_INCLUDE_NETGROUP_REF,0: "99"
+356 (343) FIREWALL_RULESET_RULE,2: "Redirection"
+357 (356) FIREWALL_RULESET_RULE_ACTION,0: "REDIRECT"
+358 (356) FIREWALL_RULESET_RULE_ACTION_REDIRECT_REF,0: "1"
+359 (356) FIREWALL_RULESET_RULE_LIMIT_FOR_ACTION_ENABLE,0: "0"
+360 (356) FIREWALL_RULESET_RULE_LIMIT_FOR_LOG_ENABLE,0: "0"
+361 (356) FIREWALL_RULESET_RULE_LIMIT_PACKETS_AVERAGE_COUNT,0: ""
+362 (356) FIREWALL_RULESET_RULE_LIMIT_PACKETS_PEAK_COUNT,0: ""
+363 (356) FIREWALL_RULESET_RULE_LOG_ENABLE,0: "0"
+364 (356) FIREWALL_RULESET_RULE_LOG_MESSAGE,0: ""
+365 (343) FIREWALL_RULESET_USAGE,0: "LANVPN"
+366 FIREWALL_SERVICEGROUP,1: "http"
+367 (366) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "1"
+368 (366) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+369 (368) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+370 (368) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "80"
+371 FIREWALL_SERVICEGROUP,2: "https"
+372 (371) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "2"
+373 (371) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+374 (373) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+375 (373) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "443"
+376 FIREWALL_SERVICEGROUP,3: "ftp-data"
+377 (376) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "3"
+378 (376) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+379 (378) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+380 (378) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_SRC_PORT,0: "20"
+381 FIREWALL_SERVICEGROUP,4: "ftp-control"
+382 (381) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "4"
+383 (381) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+384 (383) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+385 (383) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "21"
+386 FIREWALL_SERVICEGROUP,5: "ftp"
+387 (386) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,0: "3"
+388 (386) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,1: "4"
+389 (386) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "5"
+390 FIREWALL_SERVICEGROUP,6: "ssh"
+391 (390) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "6"
+392 (390) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+393 (392) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+394 (392) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "22"
+395 FIREWALL_SERVICEGROUP,7: "telnet"
+396 (395) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "7"
+397 (395) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+398 (397) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+399 (397) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "23"
+400 FIREWALL_SERVICEGROUP,8: "smtp"
+401 (400) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "8"
+402 (400) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+403 (402) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+404 (402) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "25"
+405 FIREWALL_SERVICEGROUP,9: "dns"
+406 (405) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "9"
+407 (405) FIREWALL_SERVICEGROUP_TYPE,0: "UDP"
+408 (407) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+409 (407) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "53"
+410 (405) FIREWALL_SERVICEGROUP_TYPE,1: "TCP"
+411 (410) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+412 (410) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "53"
+413 FIREWALL_SERVICEGROUP,10: "dhcp-client"
+414 (413) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "10"
+415 (413) FIREWALL_SERVICEGROUP_TYPE,0: "UDP"
+416 (415) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+417 (415) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_SRC_PORT,0: "68"
+418 FIREWALL_SERVICEGROUP,11: "dhcp-server"
+419 (418) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "11"
+420 (418) FIREWALL_SERVICEGROUP_TYPE,0: "UDP"
+421 (420) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+422 (420) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "67"
+423 FIREWALL_SERVICEGROUP,12: "dhcp"
+424 (423) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,0: "10"
+425 (423) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,1: "11"
+426 (423) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "12"
+427 FIREWALL_SERVICEGROUP,13: "tftp"
+428 (427) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "13"
+429 (427) FIREWALL_SERVICEGROUP_TYPE,0: "UDP"
+430 (429) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+431 (429) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "69"
+432 FIREWALL_SERVICEGROUP,14: "pop3"
+433 (432) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "14"
+434 (432) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+435 (434) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+436 (434) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "110"
+437 FIREWALL_SERVICEGROUP,15: "ident"
+438 (437) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "15"
+439 (437) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+440 (439) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+441 (439) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "113"
+442 FIREWALL_SERVICEGROUP,16: "nntp"
+443 (442) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "16"
+444 (442) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+445 (444) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+446 (444) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "119"
+447 FIREWALL_SERVICEGROUP,17: "ntp"
+448 (447) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "17"
+449 (447) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+450 (449) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+451 (449) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "123"
+452 (447) FIREWALL_SERVICEGROUP_TYPE,1: "UDP"
+453 (452) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+454 (452) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "123"
+455 FIREWALL_SERVICEGROUP,18: "imap"
+456 (455) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "18"
+457 (455) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+458 (457) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+459 (457) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "143"
+460 FIREWALL_SERVICEGROUP,19: "netbios-name"
+461 (460) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "19"
+462 (460) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+463 (462) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+464 (462) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "137"
+465 (460) FIREWALL_SERVICEGROUP_TYPE,1: "UDP"
+466 (465) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+467 (465) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "137"
+468 FIREWALL_SERVICEGROUP,20: "netbios-datagram"
+469 (468) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "20"
+470 (468) FIREWALL_SERVICEGROUP_TYPE,0: "UDP"
+471 (470) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+472 (470) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "138"
+473 (468) FIREWALL_SERVICEGROUP_TYPE,1: "TCP"
+474 (473) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+475 (473) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "138"
+476 FIREWALL_SERVICEGROUP,21: "netbios-session"
+477 (476) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "21"
+478 (476) FIREWALL_SERVICEGROUP_TYPE,0: "UDP"
+479 (478) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+480 (478) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "139"
+481 (476) FIREWALL_SERVICEGROUP_TYPE,1: "TCP"
+482 (481) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+483 (481) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "139"
+484 FIREWALL_SERVICEGROUP,22: "netbios"
+485 (484) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,0: "19"
+486 (484) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,1: "20"
+487 (484) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,2: "21"
+488 (484) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "22"
+489 FIREWALL_SERVICEGROUP,23: "ldap"
+490 (489) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "23"
+491 (489) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+492 (491) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+493 (491) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "389"
+494 FIREWALL_SERVICEGROUP,24: "cifs"
+495 (494) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "24"
+496 (494) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+497 (496) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+498 (496) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "445"
+499 (494) FIREWALL_SERVICEGROUP_TYPE,1: "UDP"
+500 (499) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+501 (499) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "445"
+502 FIREWALL_SERVICEGROUP,25: "ike"
+503 (502) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "25"
+504 (502) FIREWALL_SERVICEGROUP_TYPE,0: "UDP"
+505 (504) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+506 (504) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "500"
+507 FIREWALL_SERVICEGROUP,26: "syslog"
+508 (507) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "26"
+509 (507) FIREWALL_SERVICEGROUP_TYPE,0: "UDP"
+510 (509) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+511 (509) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "514"
+512 FIREWALL_SERVICEGROUP,27: "nntps"
+513 (512) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "27"
+514 (512) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+515 (514) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+516 (514) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "563"
+517 FIREWALL_SERVICEGROUP,28: "ipp"
+518 (517) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "28"
+519 (517) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+520 (519) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+521 (519) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "631"
+522 FIREWALL_SERVICEGROUP,29: "ldaps"
+523 (522) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "29"
+524 (522) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+525 (524) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+526 (524) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "636"
+527 FIREWALL_SERVICEGROUP,30: "rsync"
+528 (527) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "30"
+529 (527) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+530 (529) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+531 (529) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "873"
+532 FIREWALL_SERVICEGROUP,31: "imaps"
+533 (532) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "31"
+534 (532) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+535 (534) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+536 (534) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "993"
+537 FIREWALL_SERVICEGROUP,32: "pop3s"
+538 (537) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "32"
+539 (537) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+540 (539) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+541 (539) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "995"
+542 FIREWALL_SERVICEGROUP,33: "socks"
+543 (542) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "33"
+544 (542) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+545 (544) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+546 (544) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "1080"
+547 FIREWALL_SERVICEGROUP,34: "snmp"
+548 (547) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "34"
+549 (547) FIREWALL_SERVICEGROUP_TYPE,0: "UDP"
+550 (549) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+551 (549) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "161"
+552 (547) FIREWALL_SERVICEGROUP_TYPE,1: "UDP"
+553 (552) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+554 (552) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "162"
+555 FIREWALL_SERVICEGROUP,35: "http-proxy"
+556 (555) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "35"
+557 (555) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+558 (557) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+559 (557) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "3128"
+560 (555) FIREWALL_SERVICEGROUP_TYPE,1: "TCP"
+561 (560) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+562 (560) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "8080"
+563 FIREWALL_SERVICEGROUP,36: "http, https, ftp"
+564 (563) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,0: "1"
+565 (563) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,1: "2"
+566 (563) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,2: "5"
+567 (563) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,3: "35"
+568 (563) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "36"
+569 FIREWALL_SERVICEGROUP,37: "email"
+570 (569) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,0: "8"
+571 (569) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,1: "14"
+572 (569) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,2: "18"
+573 (569) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,3: "31"
+574 (569) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,4: "32"
+575 (569) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,5: "83"
+576 (569) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "37"
+577 FIREWALL_SERVICEGROUP,38: "ping"
+578 (577) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "38"
+579 (577) FIREWALL_SERVICEGROUP_TYPE,0: "ICMP"
+580 (579) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+581 (579) FIREWALL_SERVICEGROUP_TYPE_ICMP_NUM,0: "8"
+582 (577) FIREWALL_SERVICEGROUP_TYPE,1: "ICMP"
+583 (582) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+584 (582) FIREWALL_SERVICEGROUP_TYPE_ICMP_NUM,0: "0"
+585 FIREWALL_SERVICEGROUP,39: "icmp-basis"
+586 (585) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,0: "38"
+587 (585) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "39"
+588 (585) FIREWALL_SERVICEGROUP_TYPE,0: "ICMP"
+589 (588) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+590 (588) FIREWALL_SERVICEGROUP_TYPE_ICMP_NUM,0: "12"
+591 (585) FIREWALL_SERVICEGROUP_TYPE,1: "ICMP"
+592 (591) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+593 (591) FIREWALL_SERVICEGROUP_TYPE_ICMP_NUM,0: "3"
+594 (585) FIREWALL_SERVICEGROUP_TYPE,2: "ICMP"
+595 (594) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+596 (594) FIREWALL_SERVICEGROUP_TYPE_ICMP_NUM,0: "11"
+597 FIREWALL_SERVICEGROUP,40: "hylafax"
+598 (597) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "40"
+599 (597) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+600 (599) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: "Data"
+601 (599) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_SRC_PORT,0: "4558"
+602 (597) FIREWALL_SERVICEGROUP_TYPE,1: "TCP"
+603 (602) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: "Control"
+604 (602) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "4559"
+605 FIREWALL_SERVICEGROUP,41: "tcp"
+606 (605) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "41"
+607 (605) FIREWALL_SERVICEGROUP_TYPE,0: "OTHER"
+608 (607) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: "tcp"
+609 (607) FIREWALL_SERVICEGROUP_TYPE_OTHER_IP_PROT_NUM,0: "6"
+610 FIREWALL_SERVICEGROUP,42: "udp"
+611 (610) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "42"
+612 (610) FIREWALL_SERVICEGROUP_TYPE,0: "OTHER"
+613 (612) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: "udp"
+614 (612) FIREWALL_SERVICEGROUP_TYPE_OTHER_IP_PROT_NUM,0: "17"
+615 FIREWALL_SERVICEGROUP,43: "l2tp"
+616 (615) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "43"
+617 (615) FIREWALL_SERVICEGROUP_TYPE,0: "OTHER"
+618 (617) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: "l2tp"
+619 (617) FIREWALL_SERVICEGROUP_TYPE_OTHER_IP_PROT_NUM,0: "115"
+620 FIREWALL_SERVICEGROUP,44: "gre"
+621 (620) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "44"
+622 (620) FIREWALL_SERVICEGROUP_TYPE,0: "OTHER"
+623 (622) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: "gre"
+624 (622) FIREWALL_SERVICEGROUP_TYPE_OTHER_IP_PROT_NUM,0: "47"
+625 FIREWALL_SERVICEGROUP,45: "esp"
+626 (625) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "45"
+627 (625) FIREWALL_SERVICEGROUP_TYPE,0: "OTHER"
+628 (627) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: "esp"
+629 (627) FIREWALL_SERVICEGROUP_TYPE_OTHER_IP_PROT_NUM,0: "50"
+630 FIREWALL_SERVICEGROUP,46: "ah"
+631 (630) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "46"
+632 (630) FIREWALL_SERVICEGROUP_TYPE,0: "OTHER"
+633 (632) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: "ah"
+634 (632) FIREWALL_SERVICEGROUP_TYPE_OTHER_IP_PROT_NUM,0: "51"
+635 FIREWALL_SERVICEGROUP,47: "irc-assigned"
+636 (635) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "47"
+637 (635) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+638 (637) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: "irc"
+639 (637) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "194"
+640 FIREWALL_SERVICEGROUP,48: "ircs"
+641 (640) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "48"
+642 (640) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+643 (642) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: "ircs"
+644 (642) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "994"
+645 FIREWALL_SERVICEGROUP,49: "irc-common"
+646 (645) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "49"
+647 (645) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+648 (647) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+649 (647) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "6667"
+650 (645) FIREWALL_SERVICEGROUP_TYPE,1: "TCP"
+651 (650) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+652 (650) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "6666"
+653 FIREWALL_SERVICEGROUP,50: "bittorrent-dst"
+654 (653) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "50"
+655 (653) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+656 (655) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+657 (655) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "6881"
+658 (655) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT_END,0: "6889"
+659 FIREWALL_SERVICEGROUP,51: "bittorrent-src"
+660 (659) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "51"
+661 (659) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+662 (661) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+663 (661) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_SRC_PORT,0: "6881"
+664 (661) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_SRC_PORT_END,0: "6889"
+665 FIREWALL_SERVICEGROUP,52: "bittorrent"
+666 (665) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,0: "50"
+667 (665) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,1: "51"
+668 (665) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "52"
+669 FIREWALL_SERVICEGROUP,53: "edonkey-server"
+670 (669) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "53"
+671 (669) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+672 (671) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+673 (671) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "4662"
+674 (669) FIREWALL_SERVICEGROUP_TYPE,1: "UDP"
+675 (674) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+676 (674) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "4665"
+677 FIREWALL_SERVICEGROUP,54: "edonkey-client"
+678 (677) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "54"
+679 (677) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+680 (679) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+681 (679) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_SRC_PORT,0: "4662"
+682 (677) FIREWALL_SERVICEGROUP_TYPE,1: "TCP"
+683 (682) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+684 (682) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "4662"
+685 FIREWALL_SERVICEGROUP,55: "edonkey"
+686 (685) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,0: "53"
+687 (685) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,1: "54"
+688 (685) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "55"
+689 FIREWALL_SERVICEGROUP,56: "kazaa"
+690 (689) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "56"
+691 (689) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+692 (691) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+693 (691) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "1214"
+694 FIREWALL_SERVICEGROUP,57: "gnutella"
+695 (694) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "57"
+696 (694) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+697 (696) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: "gnutella-rtr"
+698 (696) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "6348"
+699 (694) FIREWALL_SERVICEGROUP_TYPE,1: "TCP"
+700 (699) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: "gnutella-svc"
+701 (699) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "6347"
+702 FIREWALL_SERVICEGROUP,58: "icq/aol-im"
+703 (702) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "58"
+704 (702) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+705 (704) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+706 (704) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "5190"
+707 FIREWALL_SERVICEGROUP,59: "peer 2 peer"
+708 (707) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,0: "52"
+709 (707) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,1: "55"
+710 (707) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,2: "56"
+711 (707) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,3: "57"
+712 (707) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "59"
+713 FIREWALL_SERVICEGROUP,60: "chat"
+714 (713) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,0: "58"
+715 (713) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,1: "61"
+716 (713) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,2: "64"
+717 (713) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "60"
+718 FIREWALL_SERVICEGROUP,61: "irc"
+719 (718) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,0: "47"
+720 (718) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,1: "48"
+721 (718) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,2: "49"
+722 (718) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "61"
+723 FIREWALL_SERVICEGROUP,62: "pptp-control"
+724 (723) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "62"
+725 (723) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+726 (725) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+727 (725) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "1723"
+728 FIREWALL_SERVICEGROUP,63: "pcanywhere"
+729 (728) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "63"
+730 (728) FIREWALL_SERVICEGROUP_TYPE,0: "UDP"
+731 (730) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: "pcanywhere-stat"
+732 (730) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "5632"
+733 (728) FIREWALL_SERVICEGROUP_TYPE,1: "TCP"
+734 (733) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: "pcanywhere-data"
+735 (733) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "5631"
+736 FIREWALL_SERVICEGROUP,64: "msn messenger"
+737 (736) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "64"
+738 (736) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+739 (738) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+740 (738) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "1863"
+741 FIREWALL_SERVICEGROUP,65: "printserver"
+742 (741) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "65"
+743 (741) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+744 (743) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+745 (743) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "9100"
+746 FIREWALL_SERVICEGROUP,66: "lotus notes"
+747 (746) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "66"
+748 (746) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+749 (748) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+750 (748) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "1352"
+751 FIREWALL_SERVICEGROUP,67: "rdp"
+752 (751) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "67"
+753 (751) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+754 (753) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+755 (753) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "3389"
+756 FIREWALL_SERVICEGROUP,68: "hbci"
+757 (756) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "68"
+758 (756) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+759 (758) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+760 (758) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "3000"
+761 FIREWALL_SERVICEGROUP,69: "pptp"
+762 (761) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,0: "44"
+763 (761) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,1: "62"
+764 (761) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "69"
+765 FIREWALL_SERVICEGROUP,70: "ipsec"
+766 (765) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,0: "25"
+767 (765) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,1: "45"
+768 (765) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,2: "46"
+769 (765) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,3: "82"
+770 (765) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "70"
+771 FIREWALL_SERVICEGROUP,71: "rpc"
+772 (771) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "71"
+773 (771) FIREWALL_SERVICEGROUP_TYPE,0: "UDP"
+774 (773) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+775 (773) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "111"
+776 (771) FIREWALL_SERVICEGROUP_TYPE,1: "TCP"
+777 (776) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+778 (776) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "111"
+779 FIREWALL_SERVICEGROUP,72: "ms rpc mapper"
+780 (779) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "72"
+781 (779) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+782 (781) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+783 (781) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "135"
+784 FIREWALL_SERVICEGROUP,73: "kerberos"
+785 (784) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "73"
+786 (784) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+787 (786) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+788 (786) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "88"
+789 (784) FIREWALL_SERVICEGROUP_TYPE,1: "UDP"
+790 (789) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+791 (789) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "88"
+792 FIREWALL_SERVICEGROUP,74: "ldap global catalog"
+793 (792) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "74"
+794 (792) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+795 (794) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+796 (794) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "3268"
+797 FIREWALL_SERVICEGROUP,75: "ldaps global catalog"
+798 (797) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "75"
+799 (797) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+800 (799) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+801 (799) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "3269"
+802 FIREWALL_SERVICEGROUP,76: "citrix ica"
+803 (802) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "76"
+804 (802) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+805 (804) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+806 (804) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "1494"
+807 (802) FIREWALL_SERVICEGROUP_TYPE,1: "TCP"
+808 (807) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+809 (807) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "2598"
+810 FIREWALL_SERVICEGROUP,77: "citrix browsing"
+811 (810) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "77"
+812 (810) FIREWALL_SERVICEGROUP_TYPE,0: "UDP"
+813 (812) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+814 (812) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "1604"
+815 FIREWALL_SERVICEGROUP,78: "citrix"
+816 (815) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,0: "76"
+817 (815) FIREWALL_SERVICEGROUP_INCLUDE_SERVICEGROUP_REF,1: "77"
+818 (815) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "78"
+819 FIREWALL_SERVICEGROUP,79: "elster"
+820 (819) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "79"
+821 (819) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+822 (821) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+823 (821) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "8000"
+824 FIREWALL_SERVICEGROUP,80: "sip"
+825 (824) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "80"
+826 (824) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+827 (826) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+828 (826) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "5060"
+829 (824) FIREWALL_SERVICEGROUP_TYPE,1: "UDP"
+830 (829) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+831 (829) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "5060"
+832 FIREWALL_SERVICEGROUP,81: "sips"
+833 (832) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "81"
+834 (832) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+835 (834) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+836 (834) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "5061"
+837 FIREWALL_SERVICEGROUP,82: "ike nat-t"
+838 (837) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "82"
+839 (837) FIREWALL_SERVICEGROUP_TYPE,0: "UDP"
+840 (839) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+841 (839) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "4500"
+842 FIREWALL_SERVICEGROUP,83: "smtp-submission"
+843 (842) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "83"
+844 (842) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+845 (844) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+846 (844) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "587"
+847 FIREWALL_SERVICEGROUP,84: "vnc listen"
+848 (847) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "84"
+849 (847) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+850 (849) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+851 (849) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "5500"
+852 FIREWALL_SERVICEGROUP,85: "vnc java server"
+853 (852) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "85"
+854 (852) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+855 (854) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+856 (854) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "5800"
+857 FIREWALL_SERVICEGROUP,86: "vnc server"
+858 (857) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "86"
+859 (857) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+860 (859) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+861 (859) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "5900"
+862 FIREWALL_SERVICEGROUP,87: "citrix ima"
+863 (862) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "87"
+864 (862) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+865 (864) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+866 (864) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "2512"
+867 FIREWALL_SERVICEGROUP,88: "citrix cmc"
+868 (867) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "88"
+869 (867) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+870 (869) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+871 (869) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "2513"
+872 FIREWALL_SERVICEGROUP,89: "Fernwartung Hersteller"
+873 (872) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "89"
+874 (872) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+875 (874) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+876 (874) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "5000"
+877 (874) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT_END,0: "5050"
+878 GENERATE_CHGNR,0: "226"
+879 GROUP,1: "Administratoren"
+880 (879) GROUP_ACCESS_GO_ONLINE_ALLOWED,0: "1"
+881 (879) GROUP_EMAILFILTER_BAN_FILTERLIST_REF,0: "-1"
+882 (879) GROUP_EMAIL_RELAY_RIGHTS,0: "RELAY_FROM_INTRANET"
+883 (879) GROUP_PROXY_PROFILE_REF,0: "1"
+884 GROUP,2: "Alle"
+885 (884) GROUP_ACCESS_GO_ONLINE_ALLOWED,0: "1"
+886 (884) GROUP_ACCESS_GROUPWARE_ALLOWED,0: "1"
+887 (884) GROUP_ACCESS_INFORMATION_VERSION_ALLOWED,0: "1"
+888 (884) GROUP_ACCESS_MAINPAGE_ALLOWED,0: "1"
+889 (884) GROUP_ACCESS_USERMANAGER_OWN_PROFILE_FORWARDING_ALLOWED,0: "1"
+890 (884) GROUP_ACCESS_USERMANAGER_OWN_PROFILE_GROUPWARE_ALLOWED,0: "1"
+891 (884) GROUP_ACCESS_USERMANAGER_OWN_PROFILE_SETTINGS_ALLOWED,0: "1"
+892 (884) GROUP_ACCESS_USERMANAGER_OWN_PROFILE_SORTING_ALLOWED,0: "1"
+893 (884) GROUP_ACCESS_USERMANAGER_OWN_PROFILE_SPAMFILTER_ALLOWED,0: "1"
+894 (884) GROUP_ACCESS_USERMANAGER_OWN_PROFILE_VACATION_ALLOWED,0: "1"
+895 (884) GROUP_EMAILFILTER_BAN_FILTERLIST_REF,0: "-1"
+896 (884) GROUP_EMAIL_RELAY_RIGHTS,0: "RELAY_FROM_INTRANET"
+897 (884) GROUP_PROXY_PROFILE_REF,0: "1"
+898 GROUP_ADMINISTRATOR_GROUP_REF,0: "1"
+899 GROUP_DEFAULT_GROUP_REF,0: "2"
+900 KEY_OWN,1: "mis1.net.lan"
+901 (900) KEY_OWN_FINGERPRINT_MD5,0: "B7:2D:79:24:17:8C:C4:78:CD:9A:BF:D5:20:B5:DD:26"
+902 (900) KEY_OWN_FINGERPRINT_SHA1,0: "B4:18:35:0D:71:01:67:BE:D5:7D:D7:49:35:80:43:0F:CB:9E:BC:57"
+903 (900) KEY_OWN_HASH_ALGO,0: "SHA2_256"
+904 (900) KEY_OWN_ID_X509,0: "CN=mis1.net.lan"
+905 (900) KEY_OWN_ISSUER,0: "CN=mis1.net.lan"
+906 (900) KEY_OWN_KEYSIZE,0: "2048"
+907 (900) KEY_OWN_PRIVATE_KEY,0: "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDVtDB06LaTO2Tw\n3VneaACgPG0sKXkNNZa6NAZp9hNbHiaxZeJ41NZUCHCO+7fGHWbtyMrzqdnXtvKX\nG+HOOuA3ak+rl3tX+JgcNiPEVw6to/tXYFUNTwrVdXc/npZOZTqDEElNK1PiMCVT\niECdhjUyYRMJ9upzxksNueiy2yhhnJIX9irPJ3RdvZRnKCTeGPBLEF4YDzAMGmMx\nocXDDQm4znt694wY4rd4SicPV1FNPA/lMkT6r6ZsqncMYR8COyszyKsdCbqkHVqS\n5D1rZo/MulvKRceJmbtw+ehPQwMVAdo6yTeUYxmML4KNoW7n4SjY8oV6vm5jt44w\nnKrz6hoBAgMBAAECggEAf61XIhIDOrXKmgl1buZuMU3I4BCrLRQuSHNavaXybIb4\nERbRFuX6Y1xje1mys1/PphNIBgodh77a8yMKZr9QRGt0zA0F+Uoh/wDtn5lvwJhv\nSwXX6sqlq2Cx7UWfgZkDO1jYoo1XYZToSB7ok/SiBcZvrI1kB+WZ48qF144OczoT\nmITqUGXAxzMY4y6fH5wkHLjbCkkCx7xtZDUXgqY0pWkbaGRqVSlxEZRrSx0c/ydw\nXVHhQ5LK/BZcxFnesBNgd4hL1+bVRn8LEdTP8yIZ2v+wae/RgKt1L9IiJ2PJVKzW\nFHGuysxs1ghnAqs/RCaiSNRSwtGn+leuS6NWUz4/QQKBgQDudNhZDC1lWxxjkaWY\nUrTG9ys8HOm5VOosT6hGtu2KiLN6apsSGUBLnPwzJYILbF+VtkYkP3MmEbU3ny4T\njVATkCFkgUsLPNCxazgcxJdO7J/uEiMz2Bd0W/xUwrDLhxEu+D1VZRIU6C/NxED1\nRKjpxC+Bnc+L4cN8Aj10Fa5wGQKBgQDlbSXDqCgwwPmFKSzHrHjvUoq3+kUYP3sH\ne9bpVteuIlTPEEwI9RBVBNTZGohqqqaekcpKDk2A3bYn539MbtFeKqJotA/u5kmI\nJsBTQcnudIqFnKxndAtX/NzfdAcHYpdfezhql8z7tODzWx915SnwK2Y45jiYzAGg\nfNufMxgWKQKBgBAsUqQvhEC0nkdOkJm1OleoWgFYwK12Se+5c2NgctoLjYjHBD+F\nAOxf+GcLqzdVIWKJG1bICupRFyZvoHNpCT5abzle35UiidEOwZ6J4u1SdUJzbnnM\ntUKKHqItFkqnnY2+/oH1OfW7Dcr5/aHlB0Pbr5Et/+6v4E9f44UlF1C5AoGAWYUx\nSxr+M6fYtODQHElsFtX6UrdQ5pk+hDkKLpo4JjY3KWSOhqA48zOjaeM0f4p+4Ti5\n2hOiojngrclinoxPHZM9M+WuoeUxo/Ka8OGmzWij9Gahzuw00OxVsssvQu5xakxG\nQzFgRobZj5tqtzHoH1KP3HvYcaaIq/qMJ55kM6ECgYEAn8C3qa7AOTegurVhtRFO\nvZ0ePEohDpQeqnRsscJEf36YwY7c+irIuYkTAFqZ2/UxhEFHecURyKAM4VnTsiFU\nUSARiwQ0V3NWQVXgjpRWSQxtV7pfmRB6faWA1VX2kV+BPVTQd3j4vY1DvHvuYbFo\n/knYLrPRmQRNtBbjtKPX788=\n-----END PRIVATE KEY-----\n"
+908 (900) KEY_OWN_PUBLIC_KEY,0: "-----BEGIN CERTIFICATE-----\nMIIDTTCCAjWgAwIBAgIEYJvZqTANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDEwxt\naXMxLm5ldC5sYW4wHhcNMjEwNTEyMTMzNTM3WhcNMjMwNTEyMTMzNTM3WjAXMRUw\nEwYDVQQDEwxtaXMxLm5ldC5sYW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\nAoIBAQDVtDB06LaTO2Tw3VneaACgPG0sKXkNNZa6NAZp9hNbHiaxZeJ41NZUCHCO\n+7fGHWbtyMrzqdnXtvKXG+HOOuA3ak+rl3tX+JgcNiPEVw6to/tXYFUNTwrVdXc/\nnpZOZTqDEElNK1PiMCVTiECdhjUyYRMJ9upzxksNueiy2yhhnJIX9irPJ3RdvZRn\nKCTeGPBLEF4YDzAMGmMxocXDDQm4znt694wY4rd4SicPV1FNPA/lMkT6r6ZsqncM\nYR8COyszyKsdCbqkHVqS5D1rZo/MulvKRceJmbtw+ehPQwMVAdo6yTeUYxmML4KN\noW7n4SjY8oV6vm5jt44wnKrz6hoBAgMBAAGjgaAwgZ0wHQYDVR0OBBYEFO8JKHC3\nHAN/Z2w7mwYuZF4Qw3q2MA4GA1UdDwEB/wQEAwIB/jAPBgNVHRMBAf8EBTADAQH/\nMEIGA1UdIwQ7MDmAFO8JKHC3HAN/Z2w7mwYuZF4Qw3q2oRukGTAXMRUwEwYDVQQD\nEwxtaXMxLm5ldC5sYW6CBGCb2akwFwYDVR0RBBAwDoIMbWlzMS5uZXQubGFuMA0G\nCSqGSIb3DQEBCwUAA4IBAQBKhYg43FAsn7zCFufcF2N0HTGk5RKBXh9c1ybo3Xke\nI+5BskmDNvEEt7tIvlRvC62o76VCr3Gt1sIOSWKY+m4FryvZR/yZrdvO9OfeZkAY\nSBoxczZbxeLWqWkt7henP3Fxm8X+CDHGjRuEljIW7/lI9fWP2Fezu4tMgnvbfIl3\nnIkbakOtvWDX8rC9dT++5hXbOuhHJvQyq3gD73dyZYb/HzmIZf2c1Aiv6gNrXA7Y\nctpuQaFsuss1z8IdUQgTTgJfZtFOjKbnyYEnUqYWFkMqGBrHMpcUhAun708LMOWB\n53mHNi9AFb9EqDrwWv3PjUJNDWC0Wa1DGHcXJRnjmvpn\n-----END CERTIFICATE-----\n"
+909 (900) KEY_OWN_REQUEST,0: "-----BEGIN CERTIFICATE REQUEST-----\nMIIDDjCCAfYCAQAwFzEVMBMGA1UEAxMMbWlzMS5uZXQubGFuMIIBIjANBgkqhkiG\n9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1bQwdOi2kztk8N1Z3mgAoDxtLCl5DTWWujQG\nafYTWx4msWXieNTWVAhwjvu3xh1m7cjK86nZ17bylxvhzjrgN2pPq5d7V/iYHDYj\nxFcOraP7V2BVDU8K1XV3P56WTmU6gxBJTStT4jAlU4hAnYY1MmETCfbqc8ZLDbno\nstsoYZySF/Yqzyd0Xb2UZygk3hjwSxBeGA8wDBpjMaHFww0JuM57eveMGOK3eEon\nD1dRTTwP5TJE+q+mbKp3DGEfAjsrM8irHQm6pB1akuQ9a2aPzLpbykXHiZm7cPno\nT0MDFQHaOsk3lGMZjC+CjaFu5+Eo2PKFer5uY7eOMJyq8+oaAQIDAQABoIGxMIGu\nBgkqhkiG9w0BCQ4xgaAwgZ0wHQYDVR0OBBYEFO8JKHC3HAN/Z2w7mwYuZF4Qw3q2\nMA4GA1UdDwEB/wQEAwIB/jAPBgNVHRMBAf8EBTADAQH/MEIGA1UdIwQ7MDmAFO8J\nKHC3HAN/Z2w7mwYuZF4Qw3q2oRukGTAXMRUwEwYDVQQDEwxtaXMxLm5ldC5sYW6C\nBGCb2akwFwYDVR0RBBAwDoIMbWlzMS5uZXQubGFuMA0GCSqGSIb3DQEBCwUAA4IB\nAQCBs9qZKirZlsGq+dVvl9F5xgJc55uCV5LCEnHKCdbsLvtKyjWtZzbJUnicelX6\nkLD3qdsH3soZJrWLapOoIgGLleI3MFEMPmx8JG5Z86oeXjREM4ygeyUizE5QUSqa\nkKYUHcFNFzZMMboGQNeCvYA191njXoyGB8zTnAjkja56W7PKRJAUM1RrmJNJOuwd\n+GGMSulhDJn/VKGrPVAwuIDsA9FV5MZm8TV7JK3rkmu+/WsTxqWdPOeQdj7/gKTy\nUOVawbvKQWq21dxtxT9R7YDE2Rm/m88je8VX4EDgRysSQTn7ySMOl9baRBCSA8Px\nVNyLrzaLHL/qPnJSkBZGCpmW\n-----END CERTIFICATE REQUEST-----\n"
+910 (900) KEY_OWN_SUBJECT,0: "CN=mis1.net.lan"
+911 (900) KEY_OWN_SUBJECT_ALT,0: "mis1.net.lan"
+912 (911) KEY_OWN_SUBJECT_ALT_TYPE,0: "DNS"
+913 (900) KEY_OWN_TYPE,0: "SELF"
+914 (900) KEY_OWN_VALIDFROM,0: "20210512T133537"
+915 (900) KEY_OWN_VALIDTILL,0: "20230512T133537"
+916 LICENSE_ACCEPTED,0: "1"
+917 LICENSE_EXPIRED,0: "1"
+918 LICENSE_FEATURES,1: "ANTIVIRUS"
+919 LICENSE_FEATURES,2: "FAX"
+920 LICENSE_FEATURES,3: "FIREWALL-XL"
+921 LICENSE_FEATURES,4: "MAILFILTER"
+922 LICENSE_FEATURES,5: "MAILSERVER"
+923 LICENSE_FEATURES,6: "PROXY"
+924 LICENSE_FEATURES,7: "VPN"
+925 LICENSE_FEATURES,8: "WEBFILTER"
+926 LICENSE_FEATURES,9: "WEBMAIL"
+927 LICENSE_MAXUSERS,0: "UNLIMITED"
+928 LICENSE_TIMESTAMP,0: "0"
+929 LICENSE_TRIAL_PRODUCT,0: "IBS"
+930 LOCAL_BOOT_ESP_UUID,0: "4C43-4BE9"
+931 LOCAL_BOOT_PASSWORD_MD5,0: ""
+932 LOCAL_BOOT_PASSWORD_PBKDF2,0: "grub.pbkdf2.sha512.10000.7A4E5B58E3E06F9ACF0B79C0556E7DC909EBA0FAB8102CE8214DBDB4F2734853AC1F3AE03D487A6D321BEBBDEF663CD1374D75434266F1EC30D8102109F826A6.A09FC67EAED887ADFE3F8BD7B88BA25662435FC6995C8B9CC33352DDE9A0850991A8512A8326F70A4C7D278C4E1342C94B9427B0EC8C2BAF8BCF1D39A6AD3A96"
+933 LOCAL_BOOT_RAID,0: "0"
+934 LOCAL_BOOT_RESCUE_DEVICE,0: "/dev/mapper/system-rescue"
+935 LOCAL_BOOT_ROOT_DEVICE,0: "/dev/mapper/system-root"
+936 LOCAL_BOOT_SAFEMODE,0: "0"
+937 LOCAL_CONFIG_CREATED_VERSION,0: "6.9.1"
+938 LOCAL_COUNTRY,0: "de"
+939 LOCAL_DEFAULTMSN_REF,0: "0"
+940 LOCAL_DEFAULT_THEME_REF,0: "0"
+941 LOCAL_DOMAIN,0: "net.lan"
+942 LOCAL_DOMAIN_DNS_AUTHORITY,0: "LOCAL"
+943 LOCAL_HARDWARE_DISPLAY,0: ""
+944 LOCAL_HARDWARE_ISDN,0: ""
+945 LOCAL_HARDWARE_ISDN_FIRMWARE,0: ""
+946 LOCAL_HARDWARE_MEMSIZE,0: "1020356"
+947 LOCAL_HWSERIAL,0: ""
+948 LOCAL_INSTALL_ID,0: "5f4c41cde04d4cda97111df2600ec39c21effe3a"
+949 LOCAL_INSTALL_TIME,0: "0"
+950 LOCAL_INSTALL_VERSION,0: "6.9.1"
+951 LOCAL_ISDN_AREACODE,0: ""
+952 LOCAL_ISDN_CAPI_GLOBALCALL,0: "0"
+953 LOCAL_ISDN_COUNTRYCODE,0: "49"
+954 LOCAL_ISDN_DIALPREFIX,0: ""
+955 LOCAL_ISDN_INTERNATPREFIX,0: "00"
+956 LOCAL_ISDN_LONGDISTPREFIX,0: "0"
+957 LOCAL_LOCALE,0: "de_DE"
+958 LOCAL_LOG_DEBUG_AVSCAN,0: "0"
+959 LOCAL_LOG_DEBUG_NAMED,0: "0"
+960 LOCAL_LOG_DEBUG_VPN,0: "0"
+961 LOCAL_LOG_FETCHMAIL_MAX_DAYS,0: "14"
+962 LOCAL_LOG_MAILLOG_MAX_DAYS,0: "14"
+963 LOCAL_LOG_MESSAGES_MAX_DAYS,0: "14"
+964 LOCAL_MSN,0: "0"
+965 LOCAL_MSN_COMMENT,0: ""
+966 LOCAL_NAME,0: "mis1"
+967 LOCAL_TIMEZONE,0: "Europe/Berlin"
+968 LOCAL_TIMEZONE_ISDST,0: "1"
+969 LOCAL_TRIAL_BEGIN,0: "1620826533"
+970 LOCAL_UI_FORCE_SSL,0: "1"
+971 LOCAL_VERSION,0: "6.9.1"
+972 LOGCHECK_EMAIL_TIMEPOINT,0: "25200"
+973 MON_ENABLE,0: "1"
+974 NIC,0: ""
+975 (974) NIC_COMMENT,0: "b0"
+976 (974) NIC_DRIVER,0: "virtio_net"
+977 (974) NIC_LAN_DNS_RELAYING_ALLOWED,0: "0"
+978 (974) NIC_LAN_EMAIL_RELAYING_ALLOWED,0: "0"
+979 (974) NIC_LAN_FIREWALL_RULESET_REF,0: "99"
+980 (974) NIC_LAN_IP,0: "192.168.1.1"
+981 (974) NIC_LAN_NAT_INTO,0: "0"
+982 (974) NIC_LAN_NETMASK,0: "255.255.255.0"
+983 (974) NIC_LAN_PROXY_PROFILE_REF,0: "-1"
+984 (974) NIC_MAC,0: "02:00:00:00:01:01"
+985 (974) NIC_TYPE,0: "NATLAN"
+986 NIC,1: ""
+987 (986) NIC_COMMENT,0: "b1"
+988 (986) NIC_DRIVER,0: "virtio_net"
+989 (986) NIC_MAC,0: "02:00:00:00:01:02"
+990 (986) NIC_TYPE,0: "DSLROUTER"
+991 NIC,2: ""
+992 (991) NIC_COMMENT,0: "b2"
+993 (991) NIC_DRIVER,0: "virtio_net"
+994 (991) NIC_LAN_DNS_RELAYING_ALLOWED,0: "0"
+995 (991) NIC_LAN_EMAIL_RELAYING_ALLOWED,0: "0"
+996 (991) NIC_LAN_FIREWALL_RULESET_REF,0: "1"
+997 (991) NIC_LAN_IP,0: "172.17.0.1"
+998 (991) NIC_LAN_NAT_INTO,0: "0"
+999 (991) NIC_LAN_NETMASK,0: "255.255.0.0"
+1000 (991) NIC_LAN_PROXY_PROFILE_REF,0: "-1"
+1001 (991) NIC_MAC,0: "02:00:00:00:01:03"
+1002 (991) NIC_TYPE,0: "NATLAN"
+1003 NIC,3: ""
+1004 (1003) NIC_COMMENT,0: "b3"
+1005 (1003) NIC_DRIVER,0: "virtio_net"
+1006 (1003) NIC_LAN_DNS_RELAYING_ALLOWED,0: "0"
+1007 (1003) NIC_LAN_EMAIL_RELAYING_ALLOWED,0: "0"
+1008 (1003) NIC_LAN_FIREWALL_RULESET_REF,0: "1"
+1009 (1003) NIC_LAN_IP,0: "172.24.0.1"
+1010 (1003) NIC_LAN_NAT_INTO,0: "0"
+1011 (1003) NIC_LAN_NETMASK,0: "255.255.0.0"
+1012 (1003) NIC_LAN_PROXY_PROFILE_REF,0: "-1"
+1013 (1003) NIC_MAC,0: "02:00:00:00:01:04"
+1014 (1003) NIC_TYPE,0: "NATLAN"
+1015 PINGCHECK_SERVERLIST,0: "Intra2net Server"
+1016 (1015) PINGCHECK_SERVERLIST_PREDEFINED_ID,0: "0"
+1017 (1015) PINGCHECK_SERVERLIST_SERVER,0: "0.ping.intra2net.com"
+1018 (1015) PINGCHECK_SERVERLIST_SERVER,1: "1.ping.intra2net.com"
+1019 (1015) PINGCHECK_SERVERLIST_SERVER,2: "2.ping.intra2net.com"
+1020 (1015) PINGCHECK_SERVERLIST_SERVER,3: "3.ping.intra2net.com"
+1021 (1015) PINGCHECK_SERVERLIST_SERVER,4: "4.ping.intra2net.com"
+1022 (1015) PINGCHECK_SERVERLIST_SERVER,5: "5.ping.intra2net.com"
+1023 (1015) PINGCHECK_SERVERLIST_SERVER,6: "6.ping.intra2net.com"
+1024 (1015) PINGCHECK_SERVERLIST_SERVER,7: "7.ping.intra2net.com"
+1025 PINGCHECK_SERVERLIST_DEFAULT_REF,0: "0"
+1026 PROXYLIZER_CRON,0: "0123456"
+1027 (1026) PROXYLIZER_CRON_BEGIN,0: "0"
+1028 (1026) PROXYLIZER_CRON_END,0: "86400"
+1029 (1026) PROXYLIZER_CRON_EVERY,0: "3600"
+1030 PROXY_ACCESSLIST,0: "Vordefiniert: Werbung"
+1031 (1030) PROXY_ACCESSLIST_ENTRY_COUNT,0: "39678"
+1032 (1030) PROXY_ACCESSLIST_MODE,0: "1"
+1033 (1030) PROXY_ACCESSLIST_PREDEFINED_ID,0: "0"
+1034 (1030) PROXY_ACCESSLIST_SIZETYPE,0: "0"
+1035 (1030) PROXY_ACCESSLIST_TYPE,0: "0"
+1036 PROXY_ACCESSLIST,1: "Vordefiniert: Extremismus"
+1037 (1036) PROXY_ACCESSLIST_ENTRY_COUNT,0: "678"
+1038 (1036) PROXY_ACCESSLIST_MODE,0: "1"
+1039 (1036) PROXY_ACCESSLIST_PREDEFINED_ID,0: "1"
+1040 (1036) PROXY_ACCESSLIST_SIZETYPE,0: "0"
+1041 (1036) PROXY_ACCESSLIST_TYPE,0: "0"
+1042 PROXY_ACCESSLIST,2: "Vordefiniert: Audio-Video"
+1043 (1042) PROXY_ACCESSLIST_ENTRY_COUNT,0: "7718"
+1044 (1042) PROXY_ACCESSLIST_MODE,0: "1"
+1045 (1042) PROXY_ACCESSLIST_PREDEFINED_ID,0: "2"
+1046 (1042) PROXY_ACCESSLIST_SIZETYPE,0: "0"
+1047 (1042) PROXY_ACCESSLIST_TYPE,0: "0"
+1048 PROXY_ACCESSLIST,3: "Vordefiniert: Drogen"
+1049 (1048) PROXY_ACCESSLIST_ENTRY_COUNT,0: "5012"
+1050 (1048) PROXY_ACCESSLIST_MODE,0: "1"
+1051 (1048) PROXY_ACCESSLIST_PREDEFINED_ID,0: "3"
+1052 (1048) PROXY_ACCESSLIST_SIZETYPE,0: "0"
+1053 (1048) PROXY_ACCESSLIST_TYPE,0: "0"
+1054 PROXY_ACCESSLIST,4: "Vordefiniert: Gluecksspiel"
+1055 (1054) PROXY_ACCESSLIST_ENTRY_COUNT,0: "11013"
+1056 (1054) PROXY_ACCESSLIST_MODE,0: "1"
+1057 (1054) PROXY_ACCESSLIST_PREDEFINED_ID,0: "4"
+1058 (1054) PROXY_ACCESSLIST_SIZETYPE,0: "0"
+1059 (1054) PROXY_ACCESSLIST_TYPE,0: "0"
+1060 PROXY_ACCESSLIST,5: "Vordefiniert: Hacking"
+1061 (1060) PROXY_ACCESSLIST_ENTRY_COUNT,0: "134116"
+1062 (1060) PROXY_ACCESSLIST_MODE,0: "1"
+1063 (1060) PROXY_ACCESSLIST_PREDEFINED_ID,0: "5"
+1064 (1060) PROXY_ACCESSLIST_SIZETYPE,0: "0"
+1065 (1060) PROXY_ACCESSLIST_TYPE,0: "0"
+1066 PROXY_ACCESSLIST,6: "Vordefiniert: Mail"
+1067 (1066) PROXY_ACCESSLIST_ENTRY_COUNT,0: "4958"
+1068 (1066) PROXY_ACCESSLIST_MODE,0: "1"
+1069 (1066) PROXY_ACCESSLIST_PREDEFINED_ID,0: "6"
+1070 (1066) PROXY_ACCESSLIST_SIZETYPE,0: "0"
+1071 (1066) PROXY_ACCESSLIST_TYPE,0: "0"
+1072 PROXY_ACCESSLIST,7: "Vordefiniert: Erotik"
+1073 (1072) PROXY_ACCESSLIST_ENTRY_COUNT,0: "2817405"
+1074 (1072) PROXY_ACCESSLIST_MODE,0: "1"
+1075 (1072) PROXY_ACCESSLIST_PREDEFINED_ID,0: "7"
+1076 (1072) PROXY_ACCESSLIST_SIZETYPE,0: "0"
+1077 (1072) PROXY_ACCESSLIST_TYPE,0: "0"
+1078 PROXY_ACCESSLIST,8: "Vordefiniert: Offene Proxies"
+1079 (1078) PROXY_ACCESSLIST_ENTRY_COUNT,0: "141226"
+1080 (1078) PROXY_ACCESSLIST_MODE,0: "1"
+1081 (1078) PROXY_ACCESSLIST_PREDEFINED_ID,0: "8"
+1082 (1078) PROXY_ACCESSLIST_SIZETYPE,0: "0"
+1083 (1078) PROXY_ACCESSLIST_TYPE,0: "0"
+1084 PROXY_ACCESSLIST,10: "Vordefiniert: Gewalt"
+1085 (1084) PROXY_ACCESSLIST_ENTRY_COUNT,0: "6562"
+1086 (1084) PROXY_ACCESSLIST_MODE,0: "1"
+1087 (1084) PROXY_ACCESSLIST_PREDEFINED_ID,0: "10"
+1088 (1084) PROXY_ACCESSLIST_SIZETYPE,0: "0"
+1089 (1084) PROXY_ACCESSLIST_TYPE,0: "0"
+1090 PROXY_ACCESSLIST,11: "Vordefiniert: Warez"
+1091 (1090) PROXY_ACCESSLIST_ENTRY_COUNT,0: "2010"
+1092 (1090) PROXY_ACCESSLIST_MODE,0: "1"
+1093 (1090) PROXY_ACCESSLIST_PREDEFINED_ID,0: "11"
+1094 (1090) PROXY_ACCESSLIST_SIZETYPE,0: "0"
+1095 (1090) PROXY_ACCESSLIST_TYPE,0: "0"
+1096 PROXY_ACCESSLIST,12: "Alles verboten"
+1097 (1096) PROXY_ACCESSLIST_ENTRY_COUNT,0: "0"
+1098 (1096) PROXY_ACCESSLIST_MODE,0: "0"
+1099 (1096) PROXY_ACCESSLIST_SIZETYPE,0: "1"
+1100 (1096) PROXY_ACCESSLIST_TYPE,0: "0"
+1101 PROXY_ACCESSLIST,13: "Vordefiniert: Spiele"
+1102 (1101) PROXY_ACCESSLIST_ENTRY_COUNT,0: "22438"
+1103 (1101) PROXY_ACCESSLIST_MODE,0: "1"
+1104 (1101) PROXY_ACCESSLIST_PREDEFINED_ID,0: "12"
+1105 (1101) PROXY_ACCESSLIST_SIZETYPE,0: "0"
+1106 (1101) PROXY_ACCESSLIST_TYPE,0: "0"
+1107 PROXY_ACCESSLIST,14: "Vordefiniert: Chat"
+1108 (1107) PROXY_ACCESSLIST_ENTRY_COUNT,0: "1762"
+1109 (1107) PROXY_ACCESSLIST_MODE,0: "1"
+1110 (1107) PROXY_ACCESSLIST_PREDEFINED_ID,0: "13"
+1111 (1107) PROXY_ACCESSLIST_SIZETYPE,0: "0"
+1112 (1107) PROXY_ACCESSLIST_TYPE,0: "0"
+1113 PROXY_ACCESSLIST,15: "Vordefiniert: Soziale Netzwerke"
+1114 (1113) PROXY_ACCESSLIST_ENTRY_COUNT,0: "3076"
+1115 (1113) PROXY_ACCESSLIST_MODE,0: "1"
+1116 (1113) PROXY_ACCESSLIST_PREDEFINED_ID,0: "14"
+1117 (1113) PROXY_ACCESSLIST_SIZETYPE,0: "0"
+1118 (1113) PROXY_ACCESSLIST_TYPE,0: "0"
+1119 PROXY_ALLOWED_PORTS,0: ""
+1120 (1119) PROXY_ALLOWED_PORTS_PORT,0: "21"
+1121 (1119) PROXY_ALLOWED_PORTS_PORT,2: "80"
+1122 PROXY_ALLOWED_SSL_PORTS,0: ""
+1123 (1122) PROXY_ALLOWED_SSL_PORTS_PORT,0: "443"
+1124 PROXY_CACHE_DELETE,0: "0"
+1125 PROXY_CACHE_SIZE,0: "100"
+1126 PROXY_DIGESTAUTH_ENABLE,0: "1"
+1127 PROXY_LOGS,0: "DISABLED"
+1128 PROXY_LOGS_KEEPMONTHS,0: "6"
+1129 PROXY_PORT,0: "3128"
+1130 PROXY_PROFILE,1: "Alles erlaubt"
+1131 PROXY_PROFILE,2: "Alles verboten"
+1132 (1131) PROXY_PROFILE_ACCESSLIST_REF,0: "12"
+1133 PROXY_REVERSE_LOOKUP,0: "1"
+1134 SARULES_UPDATE_CRON,0: "0123456"
+1135 (1134) SARULES_UPDATE_CRON_BEGIN,0: "27057"
+1136 SESSION_MAXLIFETIME,0: "30"
+1137 SOCKS_ENABLE,0: "0"
+1138 SOCKS_OUTGOING_FIREWALL_RULESET_REF,0: "8"
+1139 SPAMFILTER_ACTIVE,0: "1"
+1140 SPAMFILTER_BAYES_WEIGHT,0: "2"
+1141 SPAMFILTER_CALIBRATOR_CRON,0: "0123456"
+1142 (1141) SPAMFILTER_CALIBRATOR_CRON_BEGIN,0: "0"
+1143 (1141) SPAMFILTER_CALIBRATOR_CRON_END,0: "86400"
+1144 (1141) SPAMFILTER_CALIBRATOR_CRON_EVERY,0: "3600"
+1145 SPAMFILTER_DNSCHECK,0: "1"
+1146 SPAMFILTER_DNSCHECK_CALIBRATION_PROGRESS,0: "0"
+1147 SPAMFILTER_DNSCHECK_MODE,0: "ALL-RECEIVED-LINES"
+1148 SPAMFILTER_GLOBAL_POTENTIAL_SPAM_ACTION,0: "NONE"
+1149 SPAMFILTER_GLOBAL_POTENTIAL_SPAM_THRESHOLD,0: "1050"
+1150 SPAMFILTER_GLOBAL_QUARANTINE_DELETEDAYS,0: "30"
+1151 SPAMFILTER_GLOBAL_QUARANTINE_REPORT_ADMIN_REF,0: "-1"
+1152 SPAMFILTER_GLOBAL_QUARANTINE_REPORT_CRON,1: "12345"
+1153 (1152) SPAMFILTER_GLOBAL_QUARANTINE_REPORT_CRON_BEGIN,0: "27000"
+1154 SPAMFILTER_GLOBAL_QUARANTINE_REPORT_ITEM_COUNT,0: "50"
+1155 SPAMFILTER_GLOBAL_QUARANTINE_REPORT_MODE,0: "OFF"
+1156 SPAMFILTER_GLOBAL_SPAM_ACTION,0: "NONE"
+1157 SPAMFILTER_GLOBAL_SPAM_THRESHOLD,0: "1080"
+1158 SPAMFILTER_HASHCHECK,0: "1"
+1159 SPAMFILTER_RAZOR_ENABLE,0: "1"
+1160 SPAMFILTER_RECHECK_EVERY,0: "30"
+1161 SPAMFILTER_SMTP_REJECT,0: "1"
+1162 SSH_ENABLE_PAM,0: "0"
+1163 SSH_PERMIT_ROOT_LOGIN_WITH_PASSWORD,0: "1"
+1164 SSL_LOCAL_KEY_REF,0: "1"
+1165 SSL_LOCAL_STRENGTH,0: "WIN7IMAP"
+1166 SSL_REMOTE_KEY_REF,0: "1"
+1167 SSL_REMOTE_STRENGTH,0: "STRONG"
+1168 STATISTICS_CRON,0: "0123456"
+1169 (1168) STATISTICS_CRON_BEGIN,0: "0"
+1170 (1168) STATISTICS_CRON_END,0: "86400"
+1171 (1168) STATISTICS_CRON_EVERY,0: "900"
+1172 STATISTICS_DELETEDAYS,0: "730"
+1173 SYSLOG_REMOTELOG_ENABLE,0: "0"
+1174 THEME,0: "Intra2net System"
+1175 (1174) THEME_ARROW_FILENAME,0: "arrow_intranator.gif"
+1176 (1174) THEME_CSS_FILENAME,0: "intranator.css"
+1177 (1174) THEME_DEFAULTVALUES_FILENAME,0: "defaultvalues-intranator.cnf"
+1178 (1174) THEME_FAVICON_FILENAME,0: "favicon_intranator.ico"
+1179 (1174) THEME_LICENSE_AGREEMENT_FILENAME,0: "license_agreement_intranator.html"
+1180 (1174) THEME_LOGIN_FILENAME,0: "login_intranator.gif"
+1181 (1174) THEME_LOGO_FILENAME,0: "intranator.gif"
+1182 (1174) THEME_STATISTICS_FILENAME,0: "templates-intranator/arnielizer-config.xml"
+1183 THEME,1: "Xerberus"
+1184 (1183) THEME_ARROW_FILENAME,0: "arrow_xerberus.gif"
+1185 (1183) THEME_CSS_FILENAME,0: "xerberus.css"
+1186 (1183) THEME_DEFAULTVALUES_FILENAME,0: "defaultvalues-xerberus.cnf"
+1187 (1183) THEME_FAVICON_FILENAME,0: "favicon_xerberus.ico"
+1188 (1183) THEME_LICENSE_AGREEMENT_FILENAME,0: "license_agreement_xerberus.html"
+1189 (1183) THEME_LOGIN_FILENAME,0: "login_xerberus.gif"
+1190 (1183) THEME_LOGO_FILENAME,0: "xerberus.gif"
+1191 (1183) THEME_STATISTICS_FILENAME,0: "templates-xerberus/arnielizer-config.xml"
+1192 UI_HIDE_NAVIGATION_ENTRIES,0: "0"
+1193 UI_REMOTE_PORT,0: "443"
+1194 UPDATE_CRON,0: "0123456"
+1195 (1194) UPDATE_CRON_BEGIN,0: "27057"
+1196 UPDATE_EXPIRED,0: "0"
+1197 UPDATE_URL_BASE,0: "https://update.intra2net.com/"
+1198 UPDATE_VALIDATION_GROUP,0: "normal"
+1199 UPS_LOCAL_KILLPOWER_ENABLE,0: "1"
+1200 UPS_LOCAL_MINIMUM_RESTART_CAPACITY,0: "15"
+1201 USER,1: "admin"
+1202 (1201) USER_DISABLED,0: "0"
+1203 (1201) USER_FULLNAME,0: "Administrator"
+1204 (1201) USER_GROUPWARE_FOLDER_CALENDAR,0: "INBOX/Kalender"
+1205 (1201) USER_GROUPWARE_FOLDER_CONTACTS,0: "INBOX/Kontakte"
+1206 (1201) USER_GROUPWARE_FOLDER_DRAFTS,0: "INBOX/Entwürfe"
+1207 (1201) USER_GROUPWARE_FOLDER_NOTES,0: "INBOX/Notizen"
+1208 (1201) USER_GROUPWARE_FOLDER_OUTBOX,0: "INBOX/Gesendete Elemente"
+1209 (1201) USER_GROUPWARE_FOLDER_TASKS,0: "INBOX/Aufgaben"
+1210 (1201) USER_GROUPWARE_FOLDER_TRASH,0: "INBOX/Gelöschte Elemente"
+1211 (1201) USER_GROUP_MEMBER_REF,0: "1"
+1212 (1201) USER_GROUP_MEMBER_REF,1: "2"
+1213 (1201) USER_PASSWORD,0: "admin"
+1214 (1201) USER_TRASH_DELETEDAYS,0: "30"
+1215 USERSYNC_DELETED_TTL,0: "60"
+1216 VIRSCAN_BACKEND,0: "SAVAPI"
+1217 VIRSCAN_DISABLE_FPC,0: "0"
+1218 VIRSCAN_EMAIL_CLOUD_ENABLE,0: "PE-ONLY"
+1219 VIRSCAN_EMAIL_DETECT_ADWARE,0: "1"
+1220 VIRSCAN_EMAIL_ENABLE,0: "1"
+1221 VIRSCAN_EMAIL_MACRO_HEURISTIC,0: "STRONG"
+1222 VIRSCAN_EMAIL_QUARANTINE_MAX_ENTRIES,0: "15"
+1223 VIRSCAN_PROXY_AUTOMATIC_UNBLOCK,0: "60"
+1224 VIRSCAN_PROXY_CLOUD_ENABLE,0: "PE-ONLY"
+1225 VIRSCAN_PROXY_DETECT_ADWARE,0: "1"
+1226 VIRSCAN_PROXY_MACRO_HEURISTIC,0: "STRONG"
+1227 VIRSCAN_PROXY_PASS_MIMETYPES,0: ""
+1228 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,0: "audio/mpeg"
+1229 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,1: "audio/x-mpeg"
+1230 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,2: "audio/x-pn-realaudio"
+1231 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,3: "audio/x-wav"
+1232 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,4: "audio/x-realaudio"
+1233 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,6: "audio/vnd.rn-realaudio"
+1234 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,7: "video/mpeg"
+1235 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,8: "video/x-mpeg2"
+1236 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,9: "video/acorn-replay"
+1237 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,10: "video/quicktime"
+1238 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,11: "video/x-msvideo"
+1239 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,12: "video/msvideo"
+1240 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,13: "video/vnd.rn-realvideo"
+1241 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,14: "image/png"
+1242 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,15: "image/gif"
+1243 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,16: "image/tiff"
+1244 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,17: "application/vnd.ms.wms-hdr.asfv1"
+1245 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,18: "application/x-mms-framed"
+1246 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,19: "video/x-ms-asf"
+1247 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,20: "video/x-flv"
+1248 (1227) VIRSCAN_PROXY_PASS_MIMETYPES_NAME,21: "audio/x-scpls"
+1249 VIRSCAN_PROXY_TRICKLE_WORKAROUND_ONLY_TEXT,0: "1"
+1250 VIRSCAN_UPDATE_DNS_PUSH,0: "0"
+1251 VPN_CERTREQ_LIMIT,0: "16"
+1252 VPN_DEFAULT_ENCRYPTION_PROFILE_REF,0: "1"
+1253 VPN_DEFAULT_FIREWALL_RULESET_REF,0: "9"
+1254 VPN_DEFAULT_KEY_FOREIGN_CREATE_DAYS,0: "1825"
+1255 VPN_DEFAULT_KEY_OWN_REF,0: "1"
+1256 VPN_DEFAULT_MODECONFIG_BASE_IP,0: "192.168.40.1"
+1257 VPN_DEFAULT_PROXY_PROFILE_REF,0: "-1"
+1258 VPN_ENCRYPTION_PROFILE,0: "Standard (mit PFS)"
+1259 (1258) VPN_ENCRYPTION_PROFILE_IKE,0: ""
+1260 (1259) VPN_ENCRYPTION_PROFILE_IKE_ENCRYPTION,0: "AES192"
+1261 (1259) VPN_ENCRYPTION_PROFILE_IKE_GROUP,0: "MODP1536"
+1262 (1259) VPN_ENCRYPTION_PROFILE_IKE_HASH,0: "SHA2_256"
+1263 (1258) VPN_ENCRYPTION_PROFILE_IKE,1: ""
+1264 (1263) VPN_ENCRYPTION_PROFILE_IKE_ENCRYPTION,0: "AES192"
+1265 (1263) VPN_ENCRYPTION_PROFILE_IKE_GROUP,0: "MODP1024"
+1266 (1263) VPN_ENCRYPTION_PROFILE_IKE_HASH,0: "SHA2_256"
+1267 (1258) VPN_ENCRYPTION_PROFILE_IKE,2: ""
+1268 (1267) VPN_ENCRYPTION_PROFILE_IKE_ENCRYPTION,0: "AES128"
+1269 (1267) VPN_ENCRYPTION_PROFILE_IKE_GROUP,0: "MODP1536"
+1270 (1267) VPN_ENCRYPTION_PROFILE_IKE_HASH,0: "SHA"
+1271 (1258) VPN_ENCRYPTION_PROFILE_IKE,3: ""
+1272 (1271) VPN_ENCRYPTION_PROFILE_IKE_ENCRYPTION,0: "AES128"
+1273 (1271) VPN_ENCRYPTION_PROFILE_IKE_GROUP,0: "MODP1024"
+1274 (1271) VPN_ENCRYPTION_PROFILE_IKE_HASH,0: "SHA"
+1275 (1258) VPN_ENCRYPTION_PROFILE_IKE,4: ""
+1276 (1275) VPN_ENCRYPTION_PROFILE_IKE_ENCRYPTION,0: "3DES"
+1277 (1275) VPN_ENCRYPTION_PROFILE_IKE_GROUP,0: "MODP1536"
+1278 (1275) VPN_ENCRYPTION_PROFILE_IKE_HASH,0: "SHA"
+1279 (1258) VPN_ENCRYPTION_PROFILE_IKE,5: ""
+1280 (1279) VPN_ENCRYPTION_PROFILE_IKE_ENCRYPTION,0: "3DES"
+1281 (1279) VPN_ENCRYPTION_PROFILE_IKE_GROUP,0: "MODP1024"
+1282 (1279) VPN_ENCRYPTION_PROFILE_IKE_HASH,0: "SHA"
+1283 (1258) VPN_ENCRYPTION_PROFILE_IKE,6: ""
+1284 (1283) VPN_ENCRYPTION_PROFILE_IKE_ENCRYPTION,0: "3DES"
+1285 (1283) VPN_ENCRYPTION_PROFILE_IKE_GROUP,0: "MODP1536"
+1286 (1283) VPN_ENCRYPTION_PROFILE_IKE_HASH,0: "MD5"
+1287 (1258) VPN_ENCRYPTION_PROFILE_IKE,7: ""
+1288 (1287) VPN_ENCRYPTION_PROFILE_IKE_ENCRYPTION,0: "3DES"
+1289 (1287) VPN_ENCRYPTION_PROFILE_IKE_GROUP,0: "MODP1024"
+1290 (1287) VPN_ENCRYPTION_PROFILE_IKE_HASH,0: "MD5"
+1291 (1258) VPN_ENCRYPTION_PROFILE_IPSEC,0: ""
+1292 (1291) VPN_ENCRYPTION_PROFILE_IPSEC_ENCRYPTION,0: "AES192"
+1293 (1291) VPN_ENCRYPTION_PROFILE_IPSEC_HASH,0: "SHA2_256"
+1294 (1258) VPN_ENCRYPTION_PROFILE_IPSEC,1: ""
+1295 (1294) VPN_ENCRYPTION_PROFILE_IPSEC_ENCRYPTION,0: "AES128"
+1296 (1294) VPN_ENCRYPTION_PROFILE_IPSEC_HASH,0: "SHA1"
+1297 (1258) VPN_ENCRYPTION_PROFILE_IPSEC,2: ""
+1298 (1297) VPN_ENCRYPTION_PROFILE_IPSEC_ENCRYPTION,0: "3DES"
+1299 (1297) VPN_ENCRYPTION_PROFILE_IPSEC_HASH,0: "SHA1"
+1300 (1258) VPN_ENCRYPTION_PROFILE_IPSEC,3: ""
+1301 (1300) VPN_ENCRYPTION_PROFILE_IPSEC_ENCRYPTION,0: "3DES"
+1302 (1300) VPN_ENCRYPTION_PROFILE_IPSEC_HASH,0: "MD5"
+1303 (1258) VPN_ENCRYPTION_PROFILE_PFSGROUP,0: "Phase1"
+1304 VPN_ENCRYPTION_PROFILE,1: "Standard (ohne PFS)"
+1305 (1304) VPN_ENCRYPTION_PROFILE_IKE,0: ""
+1306 (1305) VPN_ENCRYPTION_PROFILE_IKE_ENCRYPTION,0: "AES192"
+1307 (1305) VPN_ENCRYPTION_PROFILE_IKE_GROUP,0: "MODP1536"
+1308 (1305) VPN_ENCRYPTION_PROFILE_IKE_HASH,0: "SHA2_256"
+1309 (1304) VPN_ENCRYPTION_PROFILE_IKE,1: ""
+1310 (1309) VPN_ENCRYPTION_PROFILE_IKE_ENCRYPTION,0: "AES192"
+1311 (1309) VPN_ENCRYPTION_PROFILE_IKE_GROUP,0: "MODP1024"
+1312 (1309) VPN_ENCRYPTION_PROFILE_IKE_HASH,0: "SHA2_256"
+1313 (1304) VPN_ENCRYPTION_PROFILE_IKE,2: ""
+1314 (1313) VPN_ENCRYPTION_PROFILE_IKE_ENCRYPTION,0: "AES128"
+1315 (1313) VPN_ENCRYPTION_PROFILE_IKE_GROUP,0: "MODP1536"
+1316 (1313) VPN_ENCRYPTION_PROFILE_IKE_HASH,0: "SHA"
+1317 (1304) VPN_ENCRYPTION_PROFILE_IKE,3: ""
+1318 (1317) VPN_ENCRYPTION_PROFILE_IKE_ENCRYPTION,0: "AES128"
+1319 (1317) VPN_ENCRYPTION_PROFILE_IKE_GROUP,0: "MODP1024"
+1320 (1317) VPN_ENCRYPTION_PROFILE_IKE_HASH,0: "SHA"
+1321 (1304) VPN_ENCRYPTION_PROFILE_IKE,4: ""
+1322 (1321) VPN_ENCRYPTION_PROFILE_IKE_ENCRYPTION,0: "3DES"
+1323 (1321) VPN_ENCRYPTION_PROFILE_IKE_GROUP,0: "MODP1536"
+1324 (1321) VPN_ENCRYPTION_PROFILE_IKE_HASH,0: "SHA"
+1325 (1304) VPN_ENCRYPTION_PROFILE_IKE,5: ""
+1326 (1325) VPN_ENCRYPTION_PROFILE_IKE_ENCRYPTION,0: "3DES"
+1327 (1325) VPN_ENCRYPTION_PROFILE_IKE_GROUP,0: "MODP1024"
+1328 (1325) VPN_ENCRYPTION_PROFILE_IKE_HASH,0: "SHA"
+1329 (1304) VPN_ENCRYPTION_PROFILE_IKE,6: ""
+1330 (1329) VPN_ENCRYPTION_PROFILE_IKE_ENCRYPTION,0: "3DES"
+1331 (1329) VPN_ENCRYPTION_PROFILE_IKE_GROUP,0: "MODP1536"
+1332 (1329) VPN_ENCRYPTION_PROFILE_IKE_HASH,0: "MD5"
+1333 (1304) VPN_ENCRYPTION_PROFILE_IKE,7: ""
+1334 (1333) VPN_ENCRYPTION_PROFILE_IKE_ENCRYPTION,0: "3DES"
+1335 (1333) VPN_ENCRYPTION_PROFILE_IKE_GROUP,0: "MODP1024"
+1336 (1333) VPN_ENCRYPTION_PROFILE_IKE_HASH,0: "MD5"
+1337 (1304) VPN_ENCRYPTION_PROFILE_IPSEC,0: ""
+1338 (1337) VPN_ENCRYPTION_PROFILE_IPSEC_ENCRYPTION,0: "AES192"
+1339 (1337) VPN_ENCRYPTION_PROFILE_IPSEC_HASH,0: "SHA2_256"
+1340 (1304) VPN_ENCRYPTION_PROFILE_IPSEC,1: ""
+1341 (1340) VPN_ENCRYPTION_PROFILE_IPSEC_ENCRYPTION,0: "AES128"
+1342 (1340) VPN_ENCRYPTION_PROFILE_IPSEC_HASH,0: "SHA1"
+1343 (1304) VPN_ENCRYPTION_PROFILE_IPSEC,2: ""
+1344 (1343) VPN_ENCRYPTION_PROFILE_IPSEC_ENCRYPTION,0: "3DES"
+1345 (1343) VPN_ENCRYPTION_PROFILE_IPSEC_HASH,0: "SHA1"
+1346 (1304) VPN_ENCRYPTION_PROFILE_IPSEC,3: ""
+1347 (1346) VPN_ENCRYPTION_PROFILE_IPSEC_ENCRYPTION,0: "3DES"
+1348 (1346) VPN_ENCRYPTION_PROFILE_IPSEC_HASH,0: "MD5"
+1349 (1304) VPN_ENCRYPTION_PROFILE_PFSGROUP,0: "None"
+1350 VPN_NAT_T,0: "1"
--- /dev/null
+# The software in this package is distributed under the GNU General
+# Public License version 2 (with a special exception described below).
+#
+# A copy of GNU General Public License (GPL) is included in this distribution,
+# in the file COPYING.GPL.
+#
+# As a special exception, if other files instantiate templates or use macros
+# or inline functions from this file, or you compile this file and link it
+# with other works to produce a work based on this file, this file
+# does not by itself cause the resulting work to be covered
+# by the GNU General Public License.
+#
+# However the source code for this file must still be made available
+# in accordance with section (3) of the GNU General Public License.
+#
+# This exception does not invalidate any other reasons why a work based
+# on this file might be covered by the GNU General Public License.
+#
+# Copyright (c) 2016-2022 Intra2net AG <info@intra2net.com>
+
+"""
+test_binary.py: unit tests for cnfvar/binary.py.
+
+Tests classes and functions in cnfvar/binary.py
+
+For help see :py:mod:`unittest`
+"""
+
+import unittest
+from textwrap import dedent
+from types import SimpleNamespace
+from unittest.mock import patch, ANY
+
+from src.cnfvar import binary
+from src.cnfvar.binary import CnfBinary
+
+
+class CnfBinaryTest(unittest.TestCase):
+ """Test class CnfBinary."""
+
+ #: return value for the run method
+ CMD_STDOUT = ""
+ CMD_STDERR = ""
+
+ def setUp(self):
+ """Set up mocks."""
+ self.CMD_STDOUT = ""
+ self.CMD_STDERR = ""
+ self._proc_patch = patch.object(binary.subprocess, "run", side_effect=self._fake_subprocess_run)
+ self._proc_mock = self._proc_patch.start()
+
+ def tearDown(self):
+ """Clean up mocks."""
+ self._proc_patch.stop()
+ self._proc_mock = None
+ self.CMD_STDOUT = ""
+ self.CMD_STDERR = ""
+
+ def test_basic_get_cnf(self):
+ """Tests basic functionality of the get_cnf wrapper."""
+ self.CMD_STDOUT = dedent("""\
+ 1 USER,1: "jake"
+ 2 (1) USER_DISABLED,0: "0"
+ 3 (1) USER_FULLNAME,0: "Jake"
+ 4 (1) USER_GROUPWARE_FOLDER_DRAFTS,0: "INBOX/Entwürfe"
+ 5 (1) USER_GROUPWARE_FOLDER_OUTBOX,0: "INBOX/Gesendete Objekte"
+ 6 (1) USER_GROUPWARE_FOLDER_TRASH,0: "INBOX/Gelöschte Elemente"
+ 7 (1) USER_GROUP_MEMBER_REF,0: "100"
+ 8 (1) USER_GROUP_MEMBER_REF,1: "2"
+ 9 (1) USER_PASSWORD,0: "test1234"
+ 11 USER,2: "jill"
+ 12 (11) USER_DISABLED,0: "0"
+ 13 (11) USER_FULLNAME,0: "Jill"
+ 14 (11) USER_GROUPWARE_FOLDER_DRAFTS,0: "INBOX/Entwürfe"
+ 15 (11) USER_GROUPWARE_FOLDER_OUTBOX,0: "INBOX/Gesendete Objekte"
+ 16 (11) USER_GROUPWARE_FOLDER_TRASH,0: "INBOX/Gelöschte Elemente"
+ 17 (11) USER_GROUP_MEMBER_REF,0: "100"
+ 18 (11) USER_GROUP_MEMBER_REF,1: "2"
+ 19 (11) USER_PASSWORD,0: "test1234"
+ """)
+
+ output = CnfBinary.get_cnf("USER", 10)
+ self.assertEqual(self.CMD_STDOUT.splitlines(), output.splitlines())
+ self._proc_mock.assert_called_once_with(["/usr/intranator/bin/get_cnf", "USER", "10"],
+ input=ANY, check=ANY, capture_output=ANY,
+ encoding='latin1', timeout=ANY)
+
+ def test_get_cnf_error_handling(self):
+ """Test that the get cnf method does some sanity checks."""
+ # cannot pass an instance without a name
+ with self.assertRaises(ValueError):
+ CnfBinary.get_cnf(None, 10)
+
+ # cannot pass an instance of an invalid type
+ with self.assertRaises(TypeError):
+ CnfBinary.get_cnf("USER", 18.45)
+
+ def test_basic_set_cnf(self):
+ """Test basic set cnf operation."""
+ input_str = """\
+ 1 FIREWALL_SERVICEGROUP,1: "http"
+ 2 (1) FIREWALL_SERVICEGROUP_PREDEFINED_ID,0: "1"
+ 3 (1) FIREWALL_SERVICEGROUP_TYPE,0: "TCP"
+ 4 (3) FIREWALL_SERVICEGROUP_TYPE_COMMENT,0: ""
+ 5 (3) FIREWALL_SERVICEGROUP_TYPE_TCPUDP_DST_PORT,0: "80"
+ """
+
+ CnfBinary.set_cnf(input_str)
+ self._proc_mock.assert_called_once_with(
+ ["/usr/intranator/bin/set_cnf"],
+ input=input_str, check=ANY, capture_output=ANY,
+ encoding="latin1", timeout=ANY)
+
+ def test_set_cnf_extra_args(self):
+ """Test that we can pass extra arguments to the set_cnf binary."""
+ input_str = """\
+ {
+ "cnf" :
+ [
+ {
+ "data" : "0",
+ "instance" : 0,
+ "number" : 1,
+ "varname" : "ACME_DEBUG_ENABLE"
+ }
+ ]
+ }
+ """
+
+ CnfBinary.set_cnf(input_str, as_json=True, fix_problems=True, discard_queue=True)
+ self._proc_mock.assert_called_once_with(
+ ["/usr/intranator/bin/set_cnf", "--json", "--fix-problems", "--discard-queue"],
+ input=input_str, check=ANY, capture_output=ANY,
+ encoding="utf8", timeout=ANY)
+
+ def _fake_subprocess_run(self, *args, **kwargs):
+ return SimpleNamespace(args=args[0], stdout=self.CMD_STDOUT,
+ stderr=self.CMD_STDERR, returncode=0)
+
+
+if __name__ == '__main__':
+ unittest.main()
--- /dev/null
+# The software in this package is distributed under the GNU General
+# Public License version 2 (with a special exception described below).
+#
+# A copy of GNU General Public License (GPL) is included in this distribution,
+# in the file COPYING.GPL.
+#
+# As a special exception, if other files instantiate templates or use macros
+# or inline functions from this file, or you compile this file and link it
+# with other works to produce a work based on this file, this file
+# does not by itself cause the resulting work to be covered
+# by the GNU General Public License.
+#
+# However the source code for this file must still be made available
+# in accordance with section (3) of the GNU General Public License.
+#
+# This exception does not invalidate any other reasons why a work based
+# on this file might be covered by the GNU General Public License.
+#
+# Copyright (c) 2016-2022 Intra2net AG <info@intra2net.com>
+
+"""
+test_model.py: unit tests for cnfvar/model.py.
+
+Tests classes and functions in cnfvar/model.py
+
+For help see :py:mod:`unittest`
+"""
+
+import unittest
+import json
+from textwrap import dedent
+from tempfile import NamedTemporaryFile
+
+from src.cnfvar import CnfList
+
+CNF_TEST_DATA = dedent("""\
+ 1 USER,1: "jake"
+ 2 (1) USER_DISABLED,0: "0"
+ 3 (1) USER_FULLNAME,0: "Jake"
+ 4 (1) USER_GROUPWARE_FOLDER_DRAFTS,0: "INBOX/Entwürfe"
+ 5 (1) USER_GROUPWARE_FOLDER_OUTBOX,0: "INBOX/Gesendete Objekte"
+ 6 (1) USER_GROUPWARE_FOLDER_TRASH,0: "INBOX/Gelöschte Elemente"
+ 7 (1) USER_GROUP_MEMBER_REF,0: "100"
+ 8 (1) USER_GROUP_MEMBER_REF,1: "2"
+ 9 (1) USER_PASSWORD,0: "test1234"
+ 11 USER,2: "jill"
+ 12 (11) USER_DISABLED,0: "0"
+ 13 (11) USER_FULLNAME,0: "Jill"
+ 14 (11) USER_GROUPWARE_FOLDER_DRAFTS,0: "INBOX/Entwürfe"
+ 15 (11) USER_GROUPWARE_FOLDER_OUTBOX,0: "INBOX/Gesendete Objekte"
+ 16 (11) USER_GROUPWARE_FOLDER_TRASH,0: "INBOX/Gelöschte Elemente"
+ 17 (11) USER_GROUP_MEMBER_REF,0: "100"
+ 18 (11) USER_GROUP_MEMBER_REF,1: "2"
+ 19 (11) USER_PASSWORD,0: "test1234"
+ 74 EMAILFILTER_BAN_FILTERLIST,0: "Vordefiniert: Alles verboten"
+ 75 (74) EMAILFILTER_BAN_FILTERLIST_ENCRYPTED,0: "BLOCK"
+ 76 (74) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS,0: ""
+ 77 (74) EMAILFILTER_BAN_FILTERLIST_FILTER_OFFICE_FILES,0: "BY_FILTERLIST"
+ 78 (74) EMAILFILTER_BAN_FILTERLIST_MIMETYPES,0: ""
+ 79 (78) EMAILFILTER_BAN_FILTERLIST_MIMETYPES_NAME,0: "text/plain"
+ 80 (78) EMAILFILTER_BAN_FILTERLIST_MIMETYPES_NAME,1: "text/html"
+ 81 (74) EMAILFILTER_BAN_FILTERLIST_MODE,0: "ALLOW"
+ 82 (74) EMAILFILTER_BAN_FILTERLIST_PREDEFINED_ID,0: "1"
+ 1196 UPDATE_EXPIRED,0: "0"
+ 1197 UPDATE_URL_BASE,0: "https://update.intra2net.com/"
+ 1198 UPDATE_VALIDATION_GROUP,0: "normal"
+ 1199 UPS_LOCAL_KILLPOWER_ENABLE,0: "1"
+ """)
+
+
+EXPECTED_CNF_DATA = dedent("""\
+ 1 USER,1: "jake"
+ 2 (1) USER_DISABLED,0: "0"
+ 3 (1) USER_FULLNAME,0: "Jake"
+ 4 (1) USER_GROUPWARE_FOLDER_DRAFTS,0: "INBOX/Entwürfe"
+ 5 (1) USER_GROUPWARE_FOLDER_OUTBOX,0: "INBOX/Gesendete Objekte"
+ 6 (1) USER_GROUPWARE_FOLDER_TRASH,0: "INBOX/Gelöschte Elemente"
+ 7 (1) USER_GROUP_MEMBER_REF,0: "100"
+ 8 (1) USER_GROUP_MEMBER_REF,1: "2"
+ 9 (1) USER_PASSWORD,0: "test1234"
+ 10 USER,2: "jane"
+ 11 (10) USER_DISABLED,0: "0"
+ 12 (10) USER_FULLNAME,0: "Jane"
+ 13 (10) USER_GROUPWARE_FOLDER_DRAFTS,0: "INBOX/Entwürfe"
+ 14 (10) USER_GROUPWARE_FOLDER_OUTBOX,0: "INBOX/Gesendete Objekte"
+ 15 (10) USER_GROUPWARE_FOLDER_TRASH,0: "INBOX/Gelöschte Elemente"
+ 16 (10) USER_GROUP_MEMBER_REF,0: "200"
+ 17 (10) USER_GROUP_MEMBER_REF,1: "2"
+ 18 (10) USER_PASSWORD,0: "test1234"
+ 19 (10) USER_GROUP_MEMBER_REF,2: "5"
+ 20 EMAILFILTER_BAN_FILTERLIST,0: "Vordefiniert: Alles verboten"
+ 21 (20) EMAILFILTER_BAN_FILTERLIST_ENCRYPTED,0: "BLOCK"
+ 22 (20) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS,0: ""
+ 23 (20) EMAILFILTER_BAN_FILTERLIST_FILTER_OFFICE_FILES,0: "BY_FILTERLIST"
+ 24 (20) EMAILFILTER_BAN_FILTERLIST_MIMETYPES,0: "" # hey this is a comment
+ 25 (24) EMAILFILTER_BAN_FILTERLIST_MIMETYPES_NAME,0: "text/plain"
+ 26 (24) EMAILFILTER_BAN_FILTERLIST_MIMETYPES_NAME,1: "text/html"
+ 27 (20) EMAILFILTER_BAN_FILTERLIST_MODE,0: "ALLOW"
+ 28 (20) EMAILFILTER_BAN_FILTERLIST_PREDEFINED_ID,0: "1"
+ 29 UPDATE_EXPIRED,0: "1"
+ 30 UPDATE_URL_BASE,0: "https://update.intra2net.com/"
+ 31 UPDATE_VALIDATION_GROUP,0: "normal"
+ 32 UPS_LOCAL_KILLPOWER_ENABLE,0: "1"
+ """)
+
+
+class TestModel(unittest.TestCase):
+ """Test the multiple capabilities of the CNF modules."""
+
+ def test_querying_and_serialization(self):
+ """Test deserializing, querying and serializing a CnfList."""
+ cnfs = CnfList.from_cnf_string(CNF_TEST_DATA)
+
+ self._modify(cnfs)
+
+ with NamedTemporaryFile() as tmpfile:
+ cnfs.to_cnf_file(tmpfile.name)
+
+ with open(tmpfile.name, "r", encoding="latin1") as f:
+ contents = f.read()
+
+ self.assertEqual(contents.splitlines(), EXPECTED_CNF_DATA.splitlines())
+
+ # make sure the result can be parsed again
+ CnfList.from_cnf_string(str(contents))
+
+ def test_querying_and_serialization_json(self):
+ """Test deserializing, querying and serializing a CnfList as JSON."""
+ # first serialize to JSON without renumbering to keep the structure
+ cnfs = CnfList.from_cnf_string(CNF_TEST_DATA)
+
+ with NamedTemporaryFile() as tmpfile:
+ cnfs.to_json_file(tmpfile.name, renumber=False)
+
+ with open(tmpfile.name, "r") as f:
+ contents = f.read()
+
+ # make sure the JSON structure is sane
+ json.loads(contents)
+
+ # now asserts that reading from JSON works
+ cnfs_2 = CnfList.from_json_string(contents)
+ self._modify(cnfs_2)
+ cnfs_2.renumber()
+
+ self.assertEqual(str(cnfs_2).splitlines(), EXPECTED_CNF_DATA.splitlines())
+
+ # make sure the result can be parsed again
+ CnfList.from_cnf_string(str(cnfs_2))
+
+ def _modify(self, cnfs):
+ """
+ Make test modifications on the list.
+
+ :param cnfs: list of cnfvars to modify
+ :type cnfs: :py:class:`CnfList`
+
+ .. note:: we unify this into a method so we can make sure that the test
+ using cnfvar strings and the test using JSON do the exactly same modifications.
+ """
+ self.assertFalse(cnfs.single_with_name("update_expired").is_enabled())
+ cnfs.single_with_name("update_expired") \
+ .enable()
+ self.assertTrue(cnfs.single_with_name("update_expired").is_enabled())
+
+ user_cnf = cnfs.first_with_value("jill")
+ user_cnf.value = "jane"
+ user_cnf.children[1].value = "Jane"
+ user_cnf.children.first_with_name("user_group_member_ref").value = "200"
+ user_cnf.add_children(("USER_GROUP_MEMBER_REF", 5))
+
+ # check correct types and equality
+ self.assertEqual(user_cnf.instance, 2)
+ self.assertEqual(user_cnf.lineno, 11)
+
+ other_cnf = cnfs.single_with_value("Vordefiniert: Alles verboten")
+ other_cnf.children \
+ .where(lambda c: c.name == "emailfilter_ban_filterlist_mimetypes") \
+ .single() \
+ .comment = "hey this is a comment"
+
+
+if __name__ == '__main__':
+ unittest.main()
--- /dev/null
+# The software in this package is distributed under the GNU General
+# Public License version 2 (with a special exception described below).
+#
+# A copy of GNU General Public License (GPL) is included in this distribution,
+# in the file COPYING.GPL.
+#
+# As a special exception, if other files instantiate templates or use macros
+# or inline functions from this file, or you compile this file and link it
+# with other works to produce a work based on this file, this file
+# does not by itself cause the resulting work to be covered
+# by the GNU General Public License.
+#
+# However the source code for this file must still be made available
+# in accordance with section (3) of the GNU General Public License.
+#
+# This exception does not invalidate any other reasons why a work based
+# on this file might be covered by the GNU General Public License.
+#
+# Copyright (c) 2016-2022 Intra2net AG <info@intra2net.com>
+
+"""
+test_model.py: unit tests for cnfvar/model.py.
+
+Tests classes and functions in cnfvar/model.py
+
+For help see :py:mod:`unittest`
+"""
+
+import unittest
+import os
+import shutil
+from textwrap import dedent
+from tempfile import NamedTemporaryFile
+from unittest.mock import patch
+
+from src.cnfvar import binary, CnfList, CnfStore, BinaryCnfStore
+from src.arnied_api import Arnied, GetCnfRet
+
+
+TEST_CNF_FILE = os.path.join(os.path.dirname(__file__), "cnfs.cnf")
+
+
+class TestStore(unittest.TestCase):
+ """Test querying, commiting and deleting cnfvars using the stores."""
+
+ def test_querying_and_updating(self):
+ """Test querying and updating cnf variables."""
+ # To avoid creating long dummy objects, we can convert our test file
+ # to the arnied API structure using our API itself
+ fake_output = CnfList.from_cnf_file(TEST_CNF_FILE) \
+ .to_api_structure()
+ fake_ret = GetCnfRet(vars=fake_output)
+
+ store = CnfStore()
+ with patch.object(Arnied, "get_cnf", return_value=fake_ret),\
+ patch.object(Arnied, "set_commit_cnf") as set_cnf_mock,\
+ patch.object(store, "_wait_for_generate"):
+ self._query_and_update(store)
+
+ args, kwargs = set_cnf_mock.call_args
+ cnf_api_input = kwargs["vars"]
+ cnf_input = CnfList.from_api_structure(cnf_api_input)
+
+ expected_input = dedent("""\
+ 1 NIC,0: ""
+ 2 (1) NIC_COMMENT,0: "b0"
+ 3 (1) NIC_DRIVER,0: "virtio_net"
+ 4 (1) NIC_LAN_DNS_RELAYING_ALLOWED,0: "1"
+ 5 (1) NIC_LAN_EMAIL_RELAYING_ALLOWED,0: "0"
+ 6 (1) NIC_LAN_FIREWALL_RULESET_REF,0: "99"
+ 7 (1) NIC_LAN_IP,0: "192.168.1.1"
+ 8 (1) NIC_LAN_NAT_INTO,0: "0"
+ 9 (1) NIC_LAN_NETMASK,0: "255.255.255.0"
+ 10 (1) NIC_LAN_PROXY_PROFILE_REF,0: "-1"
+ 11 (1) NIC_MAC,0: "02:00:00:00:01:01"
+ 12 (1) NIC_TYPE,0: "NATLAN"
+ 13 (1) NIC_LAN_PROXY_PROFILE_REF,1: "2"
+ 14 NIC,1: ""
+ 15 (14) NIC_COMMENT,0: "b18"
+ 16 (14) NIC_DRIVER,0: "virtio_net"
+ 17 (14) NIC_MAC,0: "02:00:00:00:01:02"
+ 18 (14) NIC_TYPE,0: "DSLROUTER"
+ """)
+
+ self.assertEqual(str(cnf_input).splitlines(), expected_input.splitlines())
+
+ def test_deleting_cnfvars(self):
+ """Test that we can delete top-level cnfvars."""
+ # To avoid creating long dummy objects, we can convert our test file
+ # to the arnied API structure using our API itself
+ fake_output = CnfList.from_cnf_file(TEST_CNF_FILE) \
+ .to_api_structure()
+ fake_ret = GetCnfRet(vars=fake_output)
+
+ store = CnfStore()
+ with patch.object(Arnied, "get_cnf", return_value=fake_ret):
+ cnfvars = store.query() \
+ .where(lambda c: c.name == "THEME")
+
+ with patch.object(Arnied, "set_commit_cnf") as set_cnf_mock,\
+ patch.object(store, "_wait_for_generate"):
+ store.delete(cnfvars)
+
+ args, kwargs = set_cnf_mock.call_args
+ cnf_api_input = kwargs["vars"]
+ cnf_input = CnfList.from_api_structure(cnf_api_input)
+
+ expected_input = dedent("""\
+ 1 THEME,0: "Intra2net System"
+ 2 THEME,1: "Xerberus"
+ """)
+
+ self.assertEqual([c.deleted for c in cnf_api_input],
+ [True] * len(cnfvars))
+ self.assertEqual(str(cnf_input).splitlines(), expected_input.splitlines())
+
+ def _query_and_update(self, store):
+ """
+ Query the items, update a few variables and commit the changes.
+
+ :param store: the cnf store to use
+
+ The changes done were unified in this method so that we test the same
+ thing with different stores.
+ """
+ nics = store.query() \
+ .where(lambda c: c.name == "nic")
+
+ self.assertEqual(len(nics), 4)
+
+ # test querying by instance and child
+ nic_0 = nics.single_with_instance(0)
+ nic_1 = nics.where_child(lambda c: c.name == "nic_comment" and c.value == "b1").single()
+
+ # test treating cnfvars as flags
+ self.assertFalse(nic_0.child_flag_enabled("NIC_LAN_DNS_RELAYING_ALLOWED"))
+ nic_0.enable_child_flag("nic_lan_dns_relaying_allowed")
+ self.assertTrue(nic_0.child_flag_enabled("NIC_LAN_DNS_RELAYING_ALLOWED"))
+
+ # test adding comments
+ nic_0.children.single_with_name("nic_comment").comment = "my cool nic"
+ nic_1.children.first_with_name("NIC_TYPE").comment = "a dsl router"
+
+ # test adding a reference from another cnfvar
+ proxy_profile = store.query().with_name("PROXY_PROFILE").first_with_instance(2)
+ nic_0.add_child(("nic_lan_proxy_profile_ref"), proxy_profile.instance)
+
+ # testing changing the value
+ nic_1.children.first(lambda c: c.name == "nic_comment") \
+ .value = "b18"
+ store.commit(CnfList([nic_0, nic_1], renumber=True))
+
+
+class TestBinaryStore(TestStore):
+ """Test querying, commiting and deleting cnfvars using the binary store."""
+
+ def setUp(self):
+ """Set up mocks and replacements for the real cnf binaries."""
+ with NamedTemporaryFile(delete=False) as tmp:
+ self._get_cnf_output = tmp.name
+ with NamedTemporaryFile(delete=False) as tmp:
+ self._set_cnf_input = tmp.name
+
+ with NamedTemporaryFile(delete=False, mode="w") as tmp:
+ self._fake_get_cnf = tmp.name
+ os.chmod(tmp.name, 0o775) # make file executable
+ tmp.write(dedent(f"""\
+ #!/bin/bash
+ cat "{self._get_cnf_output}"
+ """))
+ tmp.flush()
+
+ with NamedTemporaryFile(delete=False, mode="w") as tmp:
+ self._fake_set_cnf = tmp.name
+ os.chmod(tmp.name, 0o775) # make file executable
+ tmp.write(dedent(f"""\
+ #!/bin/bash
+ input=$(</dev/stdin)
+ echo $@ > "{self._set_cnf_input}"
+ echo "$input" >> "{self._set_cnf_input}"
+ """))
+ tmp.flush()
+
+ get_cnf_mock = patch.object(binary, "BIN_GET_CNF", self._fake_get_cnf)
+ self._get_cnf_mock = (get_cnf_mock, get_cnf_mock.start())
+
+ set_cnf_mock = patch.object(binary, "BIN_SET_CNF", self._fake_set_cnf)
+ self._set_cnf_mock = (set_cnf_mock, set_cnf_mock.start())
+
+ self._arnied_mock = patch.object(BinaryCnfStore, "_call_arnied")
+ self._arnied_mock.start()
+
+ def tearDown(self) -> None:
+ """Drop mocks and clean up files."""
+ self._arnied_mock.stop()
+ self._get_cnf_mock[0].stop()
+ self._set_cnf_mock[0].stop()
+
+ os.unlink(self._get_cnf_output)
+ os.unlink(self._set_cnf_input)
+ os.unlink(self._fake_get_cnf)
+ os.unlink(self._fake_set_cnf)
+
+ def test_querying_and_updating(self):
+ """Test querying and updating cnf variables."""
+ # tell our fake get_cnf to return the whole file
+ shutil.copyfile(TEST_CNF_FILE, self._get_cnf_output)
+
+ store = BinaryCnfStore()
+
+ self._query_and_update(store)
+
+ expected_input = dedent("""\
+ 1 NIC,0: ""
+ 2 (1) NIC_COMMENT,0: "b0" # my cool nic
+ 3 (1) NIC_DRIVER,0: "virtio_net"
+ 4 (1) NIC_LAN_DNS_RELAYING_ALLOWED,0: "1"
+ 5 (1) NIC_LAN_EMAIL_RELAYING_ALLOWED,0: "0"
+ 6 (1) NIC_LAN_FIREWALL_RULESET_REF,0: "99"
+ 7 (1) NIC_LAN_IP,0: "192.168.1.1"
+ 8 (1) NIC_LAN_NAT_INTO,0: "0"
+ 9 (1) NIC_LAN_NETMASK,0: "255.255.255.0"
+ 10 (1) NIC_LAN_PROXY_PROFILE_REF,0: "-1"
+ 11 (1) NIC_MAC,0: "02:00:00:00:01:01"
+ 12 (1) NIC_TYPE,0: "NATLAN"
+ 13 (1) NIC_LAN_PROXY_PROFILE_REF,1: "2"
+ 14 NIC,1: ""
+ 15 (14) NIC_COMMENT,0: "b18"
+ 16 (14) NIC_DRIVER,0: "virtio_net"
+ 17 (14) NIC_MAC,0: "02:00:00:00:01:02"
+ 18 (14) NIC_TYPE,0: "DSLROUTER" # a dsl router
+ """)
+
+ with open(self._set_cnf_input, "r") as f:
+ set_cnf_args = f.readline()
+ set_cnf_input = f.read()
+
+ self.assertEqual(set_cnf_input.splitlines(), expected_input.splitlines())
+
+ def test_deleting_cnfvars(self):
+ """Test that we can delete top-level cnfvars."""
+ # tell our fake get_cnf to return the whole file
+ shutil.copyfile(TEST_CNF_FILE, self._get_cnf_output)
+
+ store = BinaryCnfStore()
+ cnfvars = store.query() \
+ .where(lambda c: c.name == "THEME")
+
+ store.delete(cnfvars)
+
+ with open(self._set_cnf_input, "r") as f:
+ set_cnf_args = f.readline()
+ set_cnf_input = f.read()
+
+ expected_input = dedent("""\
+ 1 THEME,0: "Intra2net System"
+ 2 (1) THEME_ARROW_FILENAME,0: "arrow_intranator.gif"
+ 3 (1) THEME_CSS_FILENAME,0: "intranator.css"
+ 4 (1) THEME_DEFAULTVALUES_FILENAME,0: "defaultvalues-intranator.cnf"
+ 5 (1) THEME_FAVICON_FILENAME,0: "favicon_intranator.ico"
+ 6 (1) THEME_LICENSE_AGREEMENT_FILENAME,0: "license_agreement_intranator.html"
+ 7 (1) THEME_LOGIN_FILENAME,0: "login_intranator.gif"
+ 8 (1) THEME_LOGO_FILENAME,0: "intranator.gif"
+ 9 (1) THEME_STATISTICS_FILENAME,0: "templates-intranator/arnielizer-config.xml"
+ 10 THEME,1: "Xerberus"
+ 11 (10) THEME_ARROW_FILENAME,0: "arrow_xerberus.gif"
+ 12 (10) THEME_CSS_FILENAME,0: "xerberus.css"
+ 13 (10) THEME_DEFAULTVALUES_FILENAME,0: "defaultvalues-xerberus.cnf"
+ 14 (10) THEME_FAVICON_FILENAME,0: "favicon_xerberus.ico"
+ 15 (10) THEME_LICENSE_AGREEMENT_FILENAME,0: "license_agreement_xerberus.html"
+ 16 (10) THEME_LOGIN_FILENAME,0: "login_xerberus.gif"
+ 17 (10) THEME_LOGO_FILENAME,0: "xerberus.gif"
+ 18 (10) THEME_STATISTICS_FILENAME,0: "templates-xerberus/arnielizer-config.xml"
+ """)
+
+ self.assertIn("-x", set_cnf_args)
+ self.assertEqual(set_cnf_input.splitlines(), expected_input.splitlines())
+
+
+if __name__ == '__main__':
+ unittest.main()
--- /dev/null
+# The software in this package is distributed under the GNU General
+# Public License version 2 (with a special exception described below).
+#
+# A copy of GNU General Public License (GPL) is included in this distribution,
+# in the file COPYING.GPL.
+#
+# As a special exception, if other files instantiate templates or use macros
+# or inline functions from this file, or you compile this file and link it
+# with other works to produce a work based on this file, this file
+# does not by itself cause the resulting work to be covered
+# by the GNU General Public License.
+#
+# However the source code for this file must still be made available
+# in accordance with section (3) of the GNU General Public License.
+#
+# This exception does not invalidate any other reasons why a work based
+# on this file might be covered by the GNU General Public License.
+#
+# Copyright (c) 2016-2022 Intra2net AG <info@intra2net.com>
+
+"""
+test_arnied_api.py: unit tests for arnied_api.py.
+
+Tests classes and functions in arnied_api.py
+
+For help see :py:mod:`unittest`
+"""
+
+import sys
+import unittest
+from unittest.mock import patch, Mock, ANY
+
+from src.arnied_api import Arnied, CnfVar, GetCnfQuery, GetCnfRet
+
+
+class ArniedTest(unittest.TestCase):
+ """Test class Arnied."""
+
+ def setUp(self):
+ """Set up the mocks."""
+ self._conn_mock = Mock()
+
+ client_mock = Mock()
+ client_mock.open = Mock(return_value=self._conn_mock)
+
+ mod_mock = Mock()
+ mod_mock.Client.new_with_address = Mock(return_value=client_mock)
+
+ sys.modules["varlink"] = mod_mock
+
+ def tearDown(self):
+ """Clean up mocks."""
+ del sys.modules["varlink"]
+
+ def test_simple_get_cnf_call(self):
+ """Test that the get cnf method does some sanity checks."""
+ query = GetCnfQuery("USER", 5)
+ cnfvar = CnfVar("USER", 5, "joe", deleted=False, children=[])
+
+ self._conn_mock.GetCnf = Mock(return_value=GetCnfRet(vars=[cnfvar]))
+ retval = Arnied.get_cnf(query)
+
+ self.assertEqual(retval.vars[0], cnfvar)
+
+
+if __name__ == '__main__':
+ unittest.main()
import os
import unittest
-from src import cnfvar
+from src import cnfvar_old
#
# test data
def test_print_cnf(self):
with open(os.devnull, "w") as devnull:
- cnfvar.print_cnf(demo_cnfvar, out=devnull)
+ cnfvar_old.print_cnf(demo_cnfvar, out=devnull)
def test_parse_cnf_simple_str(self):
- cnf = cnfvar.read_cnf(demo_cnf_group)
+ cnf = cnfvar_old.read_cnf(demo_cnf_group)
with open(os.devnull, "w") as devnull:
- cnfvar.print_cnf_json(cnf, out=devnull)
+ cnfvar_old.print_cnf_json(cnf, out=devnull)
def test_parse_cnf_simple_bytes(self):
- cnf = cnfvar.read_cnf(demo_cnf_group_bytes)
+ cnf = cnfvar_old.read_cnf(demo_cnf_group_bytes)
with open(os.devnull, "w") as devnull:
- cnfvar.print_cnf_json(cnf, out=devnull)
+ cnfvar_old.print_cnf_json(cnf, out=devnull)
def test_parse_cnf_nested(self):
- cnf = cnfvar.read_cnf(demo_cnf_filter)
+ cnf = cnfvar_old.read_cnf(demo_cnf_filter)
with open(os.devnull, "w") as devnull:
- cnfvar.print_cnf_json(cnf, out=devnull)
+ cnfvar_old.print_cnf_json(cnf, out=devnull)
def test_parse_cnf_comments(self):
- cnf = cnfvar.read_cnf(demo_cnf_comments)
+ cnf = cnfvar_old.read_cnf(demo_cnf_comments)
with open(os.devnull, "w") as devnull:
- cnfvar.print_cnf_json(cnf, out=devnull)
+ cnfvar_old.print_cnf_json(cnf, out=devnull)
def test_print_cnf_garbage(self):
try:
with open(os.devnull, "w") as devnull:
- cnfvar.print_cnf(demo_invalid_cnfvar, out=devnull)
- except cnfvar.InvalidCNF:
+ cnfvar_old.print_cnf(demo_invalid_cnfvar, out=devnull)
+ except cnfvar_old.InvalidCNF:
print ("Caught the duplicate line, bravo!")
def test_read_json_str(self):
- cnf = cnfvar.read_cnf_json(demo_jsoncnf)
+ cnf = cnfvar_old.read_cnf_json(demo_jsoncnf)
with open(os.devnull, "w") as devnull:
- cnfvar.print_cnf(cnf, out=devnull)
+ cnfvar_old.print_cnf(cnf, out=devnull)
def test_read_json_bytes(self):
- cnf = cnfvar.read_cnf_json(demo_jsoncnf_bytes)
+ cnf = cnfvar_old.read_cnf_json(demo_jsoncnf_bytes)
with open(os.devnull, "w") as devnull:
- cnfvar.print_cnf(cnf, out=devnull)
+ cnfvar_old.print_cnf(cnf, out=devnull)
def test_read_json_nonascii(self):
- cnf = cnfvar.read_cnf_json(demo_nonascii)
+ cnf = cnfvar_old.read_cnf_json(demo_nonascii)
with open(os.devnull, "wb") as devnull:
- cnfvar.print_cnf(cnf, out=devnull)
+ cnfvar_old.print_cnf(cnf, out=devnull)
def test_read_json_latin1(self):
- cnf = cnfvar.read_cnf_json(demo_latin1crap)
+ cnf = cnfvar_old.read_cnf_json(demo_latin1crap)
with open(os.devnull, "wb") as devnull:
- cnfvar.print_cnf(cnf, out=devnull)
+ cnfvar_old.print_cnf(cnf, out=devnull)
def test_parse_cnf_quotes(self):
- cnf = cnfvar.read_cnf(demo_cnf_escaped_quotes)
+ cnf = cnfvar_old.read_cnf(demo_cnf_escaped_quotes)
with open(os.devnull, "w") as devnull:
- cnfvar.print_cnf_json(cnf, out=devnull)
+ cnfvar_old.print_cnf_json(cnf, out=devnull)
def test_parse_json_quotes(self):
- cnf = cnfvar.read_cnf_json(demo_json_escaped_quotes)
+ cnf = cnfvar_old.read_cnf_json(demo_json_escaped_quotes)
with open(os.devnull, "w") as devnull:
- cnfvar.print_cnf_json(cnf, out=devnull)
+ cnfvar_old.print_cnf_json(cnf, out=devnull)
class CnfVarUnittestVarnameCase(unittest.TestCase):
{"instance": 1, "varname": "hypothetical_child",
"data": "1", "number": 5, "parent": 3, "comment": None}]}
]}
- serialization = cnfvar.dump_json_string(cnf)
+ serialization = cnfvar_old.dump_json_string(cnf)
self.assertIn('DIALOUT_MODE', serialization)
self.assertIn('DIALOUT_DEFAULTPROVIDER_REF', serialization)
self.assertIn('HYPOTHETICAL_CHILD', serialization)
def test_read_cnf_lowercase(self):
"""Test that after reading, varnames are lowercase."""
- cnf = cnfvar.read_cnf_json(demo_jsoncnf.encode('latin1'))
+ cnf = cnfvar_old.read_cnf_json(demo_jsoncnf.encode('latin1'))
for parentvar in cnf['cnf']:
self.assertEqual(parentvar['varname'],
parentvar['varname'].lower())