Blame libnmstate/nm/checkpoint.py

Packit Service 0535c1
#
Packit Service 0535c1
# Copyright (c) 2018-2020 Red Hat, Inc.
Packit Service 0535c1
#
Packit Service 0535c1
# This file is part of nmstate
Packit Service 0535c1
#
Packit Service 0535c1
# This program is free software: you can redistribute it and/or modify
Packit Service 0535c1
# it under the terms of the GNU Lesser General Public License as published by
Packit Service 0535c1
# the Free Software Foundation, either version 2.1 of the License, or
Packit Service 0535c1
# (at your option) any later version.
Packit Service 0535c1
#
Packit Service 0535c1
# This program is distributed in the hope that it will be useful,
Packit Service 0535c1
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 0535c1
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 0535c1
# GNU Lesser General Public License for more details.
Packit Service 0535c1
#
Packit Service 0535c1
# You should have received a copy of the GNU Lesser General Public License
Packit Service 0535c1
# along with this program. If not, see <https://www.gnu.org/licenses/>.
Packit Service 0535c1
#
Packit Service 0535c1
Packit Service 0535c1
import logging
Packit Service 0535c1
Packit Service 0535c1
from libnmstate.error import NmstateConflictError
Packit Service 0535c1
from libnmstate.error import NmstateLibnmError
Packit Service 0535c1
from libnmstate.error import NmstatePermissionError
Packit Service 0535c1
from libnmstate.nm import connection
Packit Service 0535c1
from libnmstate.nm import common
Packit Service 0535c1
from .connection import is_activated
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def get_checkpoints(nm_client):
Packit Service 0535c1
    checkpoints = [c.get_path() for c in nm_client.get_checkpoints()]
Packit Service 0535c1
    return checkpoints
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
class CheckPoint:
Packit Service 0535c1
    def __init__(self, nm_context, timeout=60, dbuspath=None):
Packit Service 0535c1
        self._ctx = nm_context
Packit Service 0535c1
        self._timeout = timeout
Packit Service 0535c1
        self._dbuspath = dbuspath
Packit Service 0535c1
        self._timeout_source = None
Packit Service 0535c1
Packit Service 0535c1
    def __str__(self):
Packit Service 0535c1
        return self._dbuspath
Packit Service 0535c1
Packit Service 0535c1
    @staticmethod
Packit Service 0535c1
    def create(nm_context, timeout=60):
Packit Service 0535c1
        cp = CheckPoint(nm_context=nm_context, timeout=timeout)
Packit Service 0535c1
        cp._create()
Packit Service 0535c1
        return cp
Packit Service 0535c1
Packit Service 0535c1
    def _create(self):
Packit Service 0535c1
        devs = []
Packit Service 0535c1
        timeout = self._timeout
Packit Service 0535c1
        cp_flags = (
Packit Service 0535c1
            common.NM.CheckpointCreateFlags.DELETE_NEW_CONNECTIONS
Packit Service 0535c1
            | common.NM.CheckpointCreateFlags.DISCONNECT_NEW_DEVICES
Packit Service 0535c1
        )
Packit Service 0535c1
Packit Service 0535c1
        self._ctx.register_async("Create checkpoint")
Packit Service 0535c1
        self._ctx.client.checkpoint_create(
Packit Service 0535c1
            devs,
Packit Service 0535c1
            timeout,
Packit Service 0535c1
            cp_flags,
Packit Service 0535c1
            self._ctx.cancellable,
Packit Service 0535c1
            self._checkpoint_create_callback,
Packit Service 0535c1
            None,
Packit Service 0535c1
        )
Packit Service 0535c1
        self._ctx.wait_all_finish()
Packit Service 0535c1
        self._add_checkpoint_refresh_timeout()
Packit Service 0535c1
Packit Service 0535c1
    def _add_checkpoint_refresh_timeout(self):
Packit Service 0535c1
        self._timeout_source = common.GLib.timeout_source_new(
Packit Service 0535c1
            self._timeout * 500
Packit Service 0535c1
        )
Packit Service 0535c1
        self._timeout_source.set_callback(
Packit Service 0535c1
            self._refresh_checkpoint_timeout, None
Packit Service 0535c1
        )
Packit Service 0535c1
        self._timeout_source.attach(self._ctx.context)
Packit Service 0535c1
Packit Service 0535c1
    def clean_up(self):
Packit Service 0535c1
        self._remove_checkpoint_refresh_timeout()
Packit Service 0535c1
Packit Service 0535c1
    def _remove_checkpoint_refresh_timeout(self):
Packit Service 0535c1
        if self._timeout_source:
Packit Service 0535c1
            self._timeout_source.destroy()
Packit Service 0535c1
            self._timeout_source = None
Packit Service 0535c1
Packit Service 0535c1
    def _refresh_checkpoint_timeout(self, _user_data):
Packit Service 0535c1
        cancellable, cb, cb_data = (None, None, None)
Packit Service 0535c1
Packit Service 0535c1
        if self._ctx and self._ctx.client:
Packit Service 0535c1
            self._ctx.client.checkpoint_adjust_rollback_timeout(
Packit Service 0535c1
                self._dbuspath, self._timeout, cancellable, cb, cb_data
Packit Service 0535c1
            )
Packit Service 0535c1
            return common.GLib.SOURCE_CONTINUE
Packit Service 0535c1
        else:
Packit Service 0535c1
            return common.GLib.SOURCE_REMOVE
Packit Service 0535c1
Packit Service 0535c1
    def destroy(self):
Packit Service 0535c1
        if self._dbuspath:
Packit Service 0535c1
            action = f"Destroy checkpoint {self._dbuspath}"
Packit Service 0535c1
            userdata = action
Packit Service 0535c1
            self._ctx.register_async(action)
Packit Service 0535c1
            self._ctx.client.checkpoint_destroy(
Packit Service 0535c1
                self._dbuspath,
Packit Service 0535c1
                self._ctx.cancellable,
Packit Service 0535c1
                self._checkpoint_destroy_callback,
Packit Service 0535c1
                userdata,
Packit Service 0535c1
            )
Packit Service 0535c1
            self._ctx.wait_all_finish()
Packit Service 0535c1
            self.clean_up()
Packit Service 0535c1
Packit Service 0535c1
    def rollback(self):
Packit Service 0535c1
        if self._dbuspath:
Packit Service 0535c1
            action = f"Rollback to checkpoint {self._dbuspath}"
Packit Service 0535c1
            self._ctx.register_async(action)
Packit Service 0535c1
            userdata = action
Packit Service 0535c1
            self._ctx.client.checkpoint_rollback(
Packit Service 0535c1
                self._dbuspath,
Packit Service 0535c1
                self._ctx.cancellable,
Packit Service 0535c1
                self._checkpoint_rollback_callback,
Packit Service 0535c1
                userdata,
Packit Service 0535c1
            )
Packit Service 0535c1
            self._ctx.wait_all_finish()
Packit Service 0535c1
            self.clean_up()
Packit Service 0535c1
Packit Service 0535c1
    def _checkpoint_create_callback(self, client, result, data):
Packit Service 0535c1
        try:
Packit Service 0535c1
            cp = client.checkpoint_create_finish(result)
Packit Service 0535c1
            if cp:
Packit Service 0535c1
                logging.debug(
Packit Service 0535c1
                    "Checkpoint {} created for all devices".format(
Packit Service 0535c1
                        self._dbuspath
Packit Service 0535c1
                    )
Packit Service 0535c1
                )
Packit Service 0535c1
                self._dbuspath = cp.get_path()
Packit Service 0535c1
                self._ctx.finish_async("Create checkpoint")
Packit Service 0535c1
            else:
Packit Service 0535c1
                error_msg = (
Packit Service 0535c1
                    f"dbuspath={self._dbuspath} "
Packit Service 0535c1
                    f"timeout={self._timeout} "
Packit Service 0535c1
                    f"callback result={cp}"
Packit Service 0535c1
                )
Packit Service 0535c1
                self._ctx.fail(
Packit Service 0535c1
                    NmstateLibnmError(f"Checkpoint create failed: {error_msg}")
Packit Service 0535c1
                )
Packit Service 0535c1
        except common.GLib.Error as e:
Packit Service 0535c1
            if e.matches(
Packit Service 0535c1
                common.NM.ManagerError.quark(),
Packit Service 0535c1
                common.NM.ManagerError.PERMISSIONDENIED,
Packit Service 0535c1
            ):
Packit Service 0535c1
                self._ctx.fail(
Packit Service 0535c1
                    NmstatePermissionError(
Packit Service 0535c1
                        "Checkpoint create failed due to insufficient"
Packit Service 0535c1
                        " permission"
Packit Service 0535c1
                    )
Packit Service 0535c1
                )
Packit Service 0535c1
            elif e.matches(
Packit Service 0535c1
                common.NM.ManagerError.quark(),
Packit Service 0535c1
                common.NM.ManagerError.INVALIDARGUMENTS,
Packit Service 0535c1
            ):
Packit Service 0535c1
                self._ctx.fail(
Packit Service 0535c1
                    NmstateConflictError(
Packit Service 0535c1
                        "Checkpoint create failed due to a"
Packit Service 0535c1
                        " conflict with an existing checkpoint"
Packit Service 0535c1
                    )
Packit Service 0535c1
                )
Packit Service 0535c1
            else:
Packit Service 0535c1
                self._ctx.fail(
Packit Service 0535c1
                    NmstateLibnmError(f"Checkpoint create failed: error={e}")
Packit Service 0535c1
                )
Packit Service 0535c1
        except Exception as e:
Packit Service 0535c1
            self._ctx.fail(
Packit Service 0535c1
                NmstateLibnmError(f"Checkpoint create failed: error={e}")
Packit Service 0535c1
            )
Packit Service 0535c1
Packit Service 0535c1
    def _checkpoint_rollback_callback(self, client, result, data):
Packit Service 0535c1
        action = data
Packit Service 0535c1
        try:
Packit Service 0535c1
            self._check_rollback_result(client, result, self._dbuspath)
Packit Service 0535c1
            self._dbuspath = None
Packit Service 0535c1
            self._ctx.finish_async(action)
Packit Service 0535c1
        except Exception as e:
Packit Service 0535c1
            self._ctx.fail(
Packit Service 0535c1
                NmstateLibnmError(f"Checkpoint rollback failed: error={e}")
Packit Service 0535c1
            )
Packit Service 0535c1
Packit Service 0535c1
    def _check_rollback_result(self, client, result, dbus_path):
Packit Service 0535c1
        ret = client.checkpoint_rollback_finish(result)
Packit Service 0535c1
        logging.debug(f"Checkpoint {dbus_path} rollback executed")
Packit Service 0535c1
        for path in ret:
Packit Service 0535c1
            nm_dev = client.get_device_by_path(path)
Packit Service 0535c1
            iface = path if nm_dev is None else nm_dev.get_iface()
Packit Service 0535c1
            if nm_dev and (
Packit Service 0535c1
                (
Packit Service 0535c1
                    nm_dev.get_state_reason()
Packit Service 0535c1
                    == common.NM.DeviceStateReason.NEW_ACTIVATION
Packit Service 0535c1
                )
Packit Service 0535c1
                or nm_dev.get_state() == common.NM.DeviceState.IP_CONFIG
Packit Service 0535c1
            ):
Packit Service 0535c1
                nm_ac = nm_dev.get_active_connection()
Packit Service 0535c1
                if not is_activated(nm_ac, nm_dev):
Packit Service 0535c1
                    profile = connection.ConnectionProfile(self._ctx)
Packit Service 0535c1
                    profile.nmdevice = nm_dev
Packit Service 0535c1
                    action = f"Waiting for rolling back {iface}"
Packit Service 0535c1
                    self._ctx.register_async(action)
Packit Service 0535c1
                    profile.wait_dev_activation(action)
Packit Service 0535c1
            if ret[path] != 0:
Packit Service 0535c1
                logging.error(f"Interface {iface} rollback failed")
Packit Service 0535c1
            else:
Packit Service 0535c1
                logging.debug(f"Interface {iface} rollback succeeded")
Packit Service 0535c1
Packit Service 0535c1
    def _checkpoint_destroy_callback(self, client, result, data):
Packit Service 0535c1
        action = data
Packit Service 0535c1
        try:
Packit Service 0535c1
            client.checkpoint_destroy_finish(result)
Packit Service 0535c1
            logging.debug(f"Checkpoint {self._dbuspath} destroyed")
Packit Service 0535c1
            self._dbuspath = None
Packit Service 0535c1
            self._ctx.finish_async(action)
Packit Service 0535c1
        except Exception as e:
Packit Service 0535c1
            self._ctx.fail(
Packit Service 0535c1
                NmstateLibnmError(
Packit Service 0535c1
                    f"Checkpoint {self._dbuspath} destroy failed: "
Packit Service 0535c1
                    f"error={e}"
Packit Service 0535c1
                )
Packit Service 0535c1
            )