varlink: Drop varlink support

As part of our efforts to reduce dependencies, we would like to drop the
support of varlink. This is going to be dropped but if we get user
requests we can introduce it again.

Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
This commit is contained in:
Fernando Fernandez Mancera 2021-06-25 13:21:14 +02:00 committed by Gris Ge
parent 3a9825ffe8
commit ce1a4b82d8
13 changed files with 1 additions and 697 deletions

View File

@ -37,7 +37,7 @@ querying network status and applying network states.
- `./libnmstate/schemas/` Contains the API schema file.
- `./nmstatectl/` - Contains command lines tools and varlink service.
- `./nmstatectl/` - Contains command lines tools.
- `./packaging/` - Contains packaging utilities.

View File

@ -21,7 +21,6 @@ nmstatectl \- A nmstate command line tool
.br
.B nmstatectl version
.br
.B nmstatectl varlink \fR[\fIUNIX_FILE_SOCKET_PATH\fR]
.SH DESCRIPTION
.B nmstatectl\fR is created for users who want to try out nmstate without using
\fIlibnmstate\fR.
@ -113,15 +112,6 @@ checkpoint if not defined as argument.
displays nmstate version.
.RE
.PP
.B varlink
.RS
Initates the nmstate-varlink service in the specified unix file socket path
supporting the libnmstate functions (Show, Apply, Commit and Rollback).
This enables functions to be accessed via varlink command-line tool and client implementations.
Currently the service is limited to using unix file socket address only.
.PP
example: nmstatectl varlink /run/nmstate.so &
.PP
.RE
.SH OPTIONS
.B --json

View File

@ -1,154 +0,0 @@
# Varlink support for libnmstate
Varlink protocol ([Varlink.org](https://varlink.org/)) can used to communicate with libnmstate via stdin/out and varlink client implementation ([Existing Language binding](https://varlink.org/Language-Bindings#how-to-test-new-language-bindings)). Varlink protocol encodes all messages as JSON object and communicates over unix and tcp socket connections. Nmstate service is defined in io.nmstate interface address and libnmstate functions should called in as specified in varlink interface defination file `io.nmstate.varlink`. This implementation supports python3-varlink version 29.0.0 and unix socket connection only.
## Initiate nmstate varlink service
User can initate nmstate-varlink services using ```nmstatectl``` command-line tool by defining the unix socket address path. The current nmstate-varlink implementation limits only using unix sockets connection.
unix socket connection
```bash
$ sudo nmstatectl varlink /run/nmstate.so &
```
Nmstate-varlink systemd services default listening address is ```unix:/run/nmstate/nmstate.so```
systemd service initiation
```bash
$ sudo systemctl start nmstate-varlink.service
```
After the `io.nmstate` interface is resolved using varlink resolver. Methods can be called directly via stdin/out without specifying the connection.
`varlink call io.nmstate.Show`
## Nmstate basic operations using varlink
The basic functions from libnmstate (show, apply, commit and rollback) are called via varlink interface. Passing inputs to the functions only support JSON format. Below are the examples for each basic functions using varlink stdin/out and varlink client.
Using libnmstate show function via varlink (query network state)
Required for varlink stdin/out operation
```bash
$ sudo dnf install libvarlink-util
```
Varlink stdin/out:
```bash
$ sudo varlink call unix:/run/nmstate.so/io.nmstate.Show
```
Varlink python client:
```python
import varlink
with varlink.Client("unix:/run/nmstate.so").open("io.nmstate") as nmstate:
nmstate.Show()
```
JSON output: libnmstate current network is reported under "state" object.
```json
{
"log": [
{
"level": "DEBUG",
"message": "Async action: Retrieve applied config: foo started",
"time": "2020-08-05 10:22:19"
},
{
"level": "DEBUG",
"message": "Async action: Retrieve applied config: foo finished",
"time": "2020-08-05 10:22:19"
}
],
"state": {
"dns-resolver": {
"config": {
"search": [],
"server": []
},
"running": {}
},
"interfaces": [
{
"ipv4": {
"enabled": false
},
"ipv6": {
"enabled": false
},
"lldp": {
"enabled": false
},
"mac-address": "36:66:98:1D:6A:C8",
"mtu": 1500,
"name": "eth0",
"state": "down",
"type": "ethernet"
},
],
"route-rules": {
"config": []
},
"routes": {
"config": [],
"running": []
}
}
}
```
Using libnmstate apply function via varlink (query network state)
Varlink stdin/out:
```bash
$ sudo varlink call unix:/run/nmstate.so/io.nmstate.Apply '{"arguments": {"desired_state": {"interfaces": [{"name": "foo", "type": "dummy", "state": "up", "ipv4": {"enabled": false}, "ipv6": {"enabled": false}}]} } }'
```
* When using the varlink client it is not requried specify the "argument" parameter.
Varlink python client:
```python
import varlink
state = {'desired_state': {'interfaces': [{'name': 'foo', 'type': 'dummy', 'state': 'up', 'ipv4': {'enabled': False}, 'ipv6': {'enabled': False}}]} }
with varlink.Client("unix:/run/nmstate.so").open("io.nmstate") as nmstate:
nmstate.Apply(state)
```
## Error response
All error nmstate messages are encoded as JSON object format. Errors are identified with specified varlink interface description
* Example shows format of the error raised via varlink stdin/out and varlink client calling Commit method with null value.
Varlink stdin/out:
```bash
Call failed with error: NmstateValueError
{
"error_message": "No checkpoint specified or found",
"log": [
{
"level": "ERROR",
"message": "No checkpoint specified or found",
"name": "root",
"time": "2020-07-31 15:16:22"
}
]
}
```
Varlink python client:
```python
varlink.error.VarlinkError: {
'error': 'NmstateValueError',
'parameters': {
'error_message': 'No checkpoint specified or found',
'log': [
{
'time': '2020-07-31 15:18:06',
'level': 'ERROR',
'message': 'No checkpoint specified or found'
}
]
}
}
```

View File

@ -1,35 +0,0 @@
#
# Copyright (c) 2020 Red Hat, Inc.
#
# This file is part of nmstate
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 2.1 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
[Unit]
Description= Varlink service of nmstate
Documentation=https://www.nmstate.io/devel/libnmstate-varlink.html
After=multi-user.target
Requires=NetworkManager.service
ConditionPathExists=/usr/bin/nmstatectl
[Service]
Type=simple
User=root
RuntimeDirectory=nmstate
ExecStart=/usr/bin/nmstatectl varlink /run/nmstate/nmstate.so
Restart=on-failure
[Install]
WantedBy=multi-user.target

View File

@ -1,284 +0,0 @@
#
# Copyright (c) 2020 Red Hat, Inc.
#
# This file is part of nmstate
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 2.1 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
from contextlib import contextmanager
import errno
import logging
import os
import libnmstate
import libnmstate.error as libnmError
try:
import varlink
except ModuleNotFoundError:
raise libnmError.NmstateDependencyError("python3 varlink module not found")
class NmstateVarlinkLogHandler(logging.Handler):
def __init__(self):
self._log_records = list()
super().__init__()
def filter(self, record):
return True
def emit(self, record):
self._log_records.append(record)
@property
def logs(self):
"""
Return a list of dict, example:
[
{
"time": "2003-07-08 16:49:45,896",
"level": "DEBUG",
"message": "foo is changed",
}
]
"""
return [
{
"time": record.asctime,
"level": record.levelname,
"message": record.message,
}
for record in self._log_records
]
@contextmanager
def nmstate_varlink_logger():
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
handler = NmstateVarlinkLogHandler()
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
try:
yield handler
finally:
logger.removeHandler(handler)
def validate_method_arguments(user_args, method_args):
"""
Returns dictionary with validated arguments values.
"""
kwargs = {}
for arg in user_args.keys():
if arg not in method_args or user_args[arg] == {}:
raise varlink.InvalidParameter(arg)
if user_args[arg] is not None:
kwargs[arg] = user_args[arg]
return kwargs
def gen_varlink_server(address):
"""
Returns varlink server object
Checks if the varlink address already in use.
"""
address = "unix:" + address
server = varlink.ThreadingServer(
address, ServiceRequestHandler, bind_and_activate=False
)
server.allow_reuse_address = True
try:
server.server_bind()
server.server_activate()
except OSError as exception:
server.shutdown()
server.server_close()
if exception.errno == errno.EADDRINUSE:
server = varlink.ThreadingServer(
address, ServiceRequestHandler, bind_and_activate=True
)
else:
logging.error(exception.strerror)
raise exception
return server
def start_varlink_server(address):
"""
Runs the varlink server in the specified the file path
"""
varlink_server = gen_varlink_server(address)
try:
with varlink_server as server:
server.serve_forever()
except Exception as exception:
logging.error(str(exception))
varlink_server.shutdown()
finally:
varlink_server.server_close()
class NmstateError(varlink.VarlinkError):
def __init__(self, message, logs):
varlink.VarlinkError.__init__(
self,
{
"error": self.__class__.__name__,
"parameters": {
"error_message": message,
"log": logs,
},
},
)
class NmstateValueError(NmstateError):
pass
class NmstatePermissionError(NmstateError):
pass
class NmstateConflictError(NmstateError):
pass
class NmstateLibnmError(NmstateError):
pass
class NmstateVerificationError(NmstateError):
pass
class NmstateNotImplementedError(NmstateError):
pass
class NmstateInternalError(NmstateError):
pass
class NmstateDependencyError(NmstateError):
pass
class ServiceRequestHandler(varlink.RequestHandler):
service = varlink.Service(
vendor="Red Hat",
product="Nmstate",
version=libnmstate.__version__,
url="https://www.nmstate.io",
interface_dir=os.path.dirname(__file__),
namespaced=False,
)
@ServiceRequestHandler.service.interface("io.nmstate")
class NmstateVarlinkService:
def Show(self, arguments):
"""
Reports the state data on the system
"""
with nmstate_varlink_logger() as log_handler:
method_args = ["include_status_data"]
show_kwargs = validate_method_arguments(arguments, method_args)
try:
configured_state = libnmstate.show(**show_kwargs)
return {"state": configured_state, "log": log_handler.logs}
except libnmstate.error.NmstateValueError as exception:
logging.error(str(exception))
raise NmstateValueError(str(exception), log_handler.logs)
def ShowRunningConfig(self, arguments):
with nmstate_varlink_logger() as log_handler:
method_args = []
validate_method_arguments(arguments, method_args)
try:
configured_state = libnmstate.show_running_config()
return {"state": configured_state, "log": log_handler.logs}
except libnmstate.error.NmstateValueError as exception:
logging.error(str(exception))
raise NmstateValueError(str(exception), log_handler.logs)
def Apply(self, arguments):
"""
Apply desired state declared in json format
which is parsed as dictionary
"""
with nmstate_varlink_logger() as log_handler:
method_args = [
"desired_state",
"verify_change",
"commit",
"rollback_timeout",
"save_to_disk",
]
apply_kwargs = validate_method_arguments(arguments, method_args)
if "desired_state" not in apply_kwargs.keys():
logging.error("Desired_state not specified")
raise NmstateValueError(
"desired_state: No state specified", log_handler.logs
)
try:
libnmstate.apply(**apply_kwargs)
return {"log": log_handler.logs}
except TypeError as exception:
logging.error(str(exception), log_handler.logs)
raise varlink.InvalidParameter(exception)
except libnmstate.error.NmstatePermissionError as exception:
logging.error(str(exception))
raise NmstatePermissionError(str(exception), log_handler.logs)
except libnmstate.error.NmstateValueError as exception:
logging.error(str(exception))
raise NmstateValueError(str(exception), log_handler.logs)
except libnmstate.error.NmstateConflictError as exception:
logging.error(str(exception))
raise NmstateConflictError(str(exception), log_handler.logs)
except libnmstate.error.NmstateVerificationError as exception:
logging.error(str(exception))
raise NmstateVerificationError(
str(exception), log_handler.logs
)
def Commit(self, arguments):
"""
Commits the checkpoint
"""
with nmstate_varlink_logger() as log_handler:
method_args = ["checkpoint"]
commit_kwargs = validate_method_arguments(arguments, method_args)
try:
libnmstate.commit(**commit_kwargs)
return {"log": log_handler.logs}
except libnmstate.error.NmstateValueError as exception:
logging.error(str(exception))
raise NmstateValueError(str(exception), log_handler.logs)
def Rollback(self, arguments):
"""
Roll back to the checkpoint
"""
with nmstate_varlink_logger() as log_handler:
method_args = ["checkpoint"]
rollback_kwargs = validate_method_arguments(arguments, method_args)
try:
libnmstate.rollback(**rollback_kwargs)
return {"log": log_handler.logs}
except libnmstate.error.NmstateValueError as exception:
logging.error(str(exception))
raise NmstateValueError(str(exception), log_handler.logs)

View File

@ -38,7 +38,6 @@ from libnmstate.schema import Interface
from libnmstate.schema import InterfaceIP
from libnmstate.schema import Route
from libnmstate.schema import RouteRule
from nmstatectl.nmstate_varlink import start_varlink_server
def main():
@ -57,7 +56,6 @@ def main():
setup_subcommand_apply(subparsers)
setup_subcommand_show(subparsers)
setup_subcommand_version(subparsers)
setup_subcommand_varlink(subparsers)
setup_subcommand_gen_config(subparsers)
parser.add_argument(
"--version", action="store_true", help="Display nmstate version"
@ -244,16 +242,6 @@ def setup_subcommand_version(subparsers):
parser_version.set_defaults(func=version)
def setup_subcommand_varlink(subparsers):
parser_varlink = subparsers.add_parser(
"varlink", help="Varlink support for libnmstate"
)
parser_varlink.add_argument(
"address", type=str, help="Unix socket address e.g: /run/nmstate"
)
parser_varlink.set_defaults(func=run_varlink_server)
def setup_subcommand_gen_config(subparsers):
parser_gc = subparsers.add_parser("gc", help="Generate configurations")
parser_gc.add_argument(
@ -358,13 +346,6 @@ def apply(args):
return 1
def run_varlink_server(args):
try:
start_varlink_server(args.address)
except Exception as exception:
logging.exception(exception)
def run_gen_config(args):
if args.file:
for statefile in args.file:

View File

@ -31,8 +31,6 @@ RUN dnf update -y && \
python3-requests \
python3-docopt \
python3-nispor \
python3-varlink \
libvarlink-util \
tcpreplay \
wpa_supplicant \
hostapd \

View File

@ -31,9 +31,7 @@ RUN dnf -y install dnf-plugins-core epel-release && \
python3-requests \
python3-docopt \
python3-nispor \
python3-varlink \
tcpreplay \
libvarlink-util \
wpa_supplicant \
hostapd \
&& \

View File

@ -37,8 +37,6 @@ RUN dnf -y install dnf-plugins-core && \
gobject-introspection-devel \
python3-devel \
python3-nispor \
python3-varlink \
libvarlink-util \
make && \
\
dnf clean all && \

View File

@ -58,20 +58,11 @@ This package contains the nmstate plugin for OVS database manipulation.
%prep
%setup -q
%preun
%systemd_preun nmstate-varlink.service
%build
%py3_build
%install
%py3_install
mkdir -p %{buildroot}%{_unitdir}
install -p -m 644 %{buildroot}%{python3_sitelib}/nmstatectl/nmstate-varlink.service \
%{buildroot}%{_unitdir}/nmstate-varlink.service
%post
%systemd_post nmstate-varlink.service
%files
%doc README.md
@ -86,7 +77,6 @@ install -p -m 644 %{buildroot}%{python3_sitelib}/nmstatectl/nmstate-varlink.serv
%{python3_sitelib}/%{libname}
%exclude %{python3_sitelib}/%{libname}/plugins/nmstate_plugin_*
%exclude %{python3_sitelib}/%{libname}/plugins/__pycache__/nmstate_plugin_*
%{_unitdir}/nmstate-varlink.service
%files -n nmstate-plugin-ovsdb
%{python3_sitelib}/%{libname}/plugins/nmstate_plugin_ovsdb*

View File

@ -9,5 +9,4 @@ jsonschema
PyGObject
PyYAML
setuptools
varlink
nispor>=1.1.0

View File

@ -51,7 +51,6 @@ setup(
},
package_data={
"libnmstate": ["schemas/operational-state.yaml", "VERSION"],
"nmstatectl": ["io.nmstate.varlink", "nmstate-varlink.service"],
},
data_files=gen_manpage(),
)

View File

@ -1,176 +0,0 @@
#
# Copyright (c) 2020 Red Hat, Inc.
#
# This file is part of nmstate
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 2.1 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
import pytest
import varlink
import threading
import subprocess
import sys
import json
import libnmstate
import os
import time
from nmstatectl.nmstate_varlink import gen_varlink_server
from libnmstate.schema import Interface
from libnmstate.schema import InterfaceType
from libnmstate.schema import InterfaceState
from libnmstate.schema import InterfaceIPv4
from libnmstate.schema import InterfaceIPv6
VARLINK_INTERFACE = "io.nmstate"
APPLY_STATE = {
Interface.KEY: [
{
Interface.NAME: "varlink_test",
Interface.TYPE: InterfaceType.DUMMY,
Interface.STATE: InterfaceState.UP,
Interface.IPV4: {InterfaceIPv4.ENABLED: False},
Interface.IPV6: {InterfaceIPv6.ENABLED: False},
}
]
}
@pytest.fixture(
params=["/run/nmstate"],
ids=["unix_socket_connection"],
)
def server(request):
server = gen_varlink_server(request.param)
thread_server = threading.Thread(target=server.serve_forever, daemon=True)
thread_server.start()
yield server
server.shutdown()
server.server_close()
@pytest.fixture(
params=["unix:/run/nmstate/nmstate.so"],
ids=["systemd_unix_connection"],
)
def systemd_service(request):
if os.system("systemctl is-active nmstate-varlink.service") != 0:
os.system("systemctl start nmstate-varlink.service")
time.sleep(1)
yield request.param
os.system("systemctl stop nmstate-varlink.service")
def _format_address(server_address):
return f"unix:{server_address}"
def test_varlink_show(server):
lib_state = libnmstate.show()
with varlink.Client(_format_address(server.server_address)).open(
VARLINK_INTERFACE, namespaced=False
) as con:
varlink_state = con._call("Show")
assert varlink_state["state"] == lib_state
def test_varlink_show_running_config(server):
lib_state = libnmstate.show_running_config()
with varlink.Client(_format_address(server.server_address)).open(
VARLINK_INTERFACE, namespaced=False
) as con:
varlink_state = con._call("ShowRunningConfig")
assert varlink_state["state"] == lib_state
def test_varlink_apply_state(server):
with varlink.Client(_format_address(server.server_address)).open(
VARLINK_INTERFACE, namespaced=False
) as con:
con._call("Apply", {"desired_state": APPLY_STATE})
lib_state = libnmstate.show()
assert any(
interface
for interface in lib_state[Interface.KEY]
if interface[Interface.NAME]
== APPLY_STATE[Interface.KEY][0][Interface.NAME]
)
APPLY_STATE[Interface.KEY][0][Interface.STATE] = InterfaceState.DOWN
libnmstate.apply(APPLY_STATE)
def test_varlink_apply_with_none_state(server):
apply_state = {"desired_state": {}}
with varlink.Client(_format_address(server.server_address)).open(
VARLINK_INTERFACE, namespaced=False
) as con:
with pytest.raises(varlink.InvalidParameter):
con._call("Apply", apply_state)
def test_varlink_commit_error(server):
with varlink.Client(_format_address(server.server_address)).open(
VARLINK_INTERFACE, namespaced=False
) as con:
with pytest.raises(varlink.VarlinkError):
con._call("Commit", None)
def test_varlink_rollback_error(server):
with varlink.Client(_format_address(server.server_address)).open(
VARLINK_INTERFACE, namespaced=False
) as con:
with pytest.raises(varlink.VarlinkError):
con._call("Rollback", None)
def test_varlink_activation_mode():
lib_state = libnmstate.show()
command = (
"varlink --activate='nmstatectl varlink /run/nmstate' "
+ "call io.nmstate.Show"
)
output = subprocess.check_output(command, shell=True).decode(
sys.stdout.encoding
)
parsed_out = json.loads(output)
assert parsed_out["state"] == lib_state
def test_systemd_varlink_show(systemd_service):
lib_state = libnmstate.show()
with varlink.Client(systemd_service).open(
VARLINK_INTERFACE, namespaced=False
) as con:
varlink_state = con._call("Show")
assert varlink_state["state"] == lib_state
def test_systemd_varlink_apply_state(systemd_service):
APPLY_STATE[Interface.KEY][0][Interface.STATE] = InterfaceState.UP
with varlink.Client(systemd_service).open(
VARLINK_INTERFACE, namespaced=False
) as con:
con._call("Apply", {"desired_state": APPLY_STATE})
lib_state = libnmstate.show()
assert any(
interface
for interface in lib_state[Interface.KEY]
if interface[Interface.NAME]
== APPLY_STATE[Interface.KEY][0][Interface.NAME]
)
APPLY_STATE[Interface.KEY][0][Interface.STATE] = InterfaceState.DOWN
libnmstate.apply(APPLY_STATE)