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