Build Windows Templates in RHV
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import array
|
||||
import struct
|
||||
import fcntl
|
||||
import socket
|
||||
|
||||
from .utils import Util
|
||||
|
||||
ETHTOOL_GPERMADDR = 0x00000020
|
||||
SIOCETHTOOL = 0x8946
|
||||
MAX_ADDR_LEN = 32
|
||||
IFNAMESIZ = 16
|
||||
|
||||
|
||||
def get_perm_addr(ifname):
|
||||
"""
|
||||
Return the Permanent address value for the specified interface using the
|
||||
ETHTOOL_GPERMADDR ioctl command.
|
||||
|
||||
Please for further documentation, see:
|
||||
https://github.com/torvalds/linux/blob/master/include/uapi/linux/ethtool.h#L734
|
||||
https://github.com/torvalds/linux/blob/master/include/uapi/linux/ethtool.h#L1388
|
||||
https://git.kernel.org/pub/scm/network/ethtool/ethtool.git/tree/ethtool.c#n4172
|
||||
"""
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sockfd = sock.fileno()
|
||||
ifname = ifname.encode("utf-8")
|
||||
if len(ifname) > IFNAMESIZ:
|
||||
return None
|
||||
|
||||
ecmd = array.array(
|
||||
"B",
|
||||
struct.pack(
|
||||
"II%is" % MAX_ADDR_LEN,
|
||||
ETHTOOL_GPERMADDR,
|
||||
MAX_ADDR_LEN,
|
||||
b"\x00" * MAX_ADDR_LEN,
|
||||
),
|
||||
)
|
||||
ifreq = struct.pack("%isP" % IFNAMESIZ, ifname, ecmd.buffer_info()[0])
|
||||
|
||||
fcntl.ioctl(sockfd, SIOCETHTOOL, ifreq)
|
||||
try:
|
||||
res = ecmd.tobytes()
|
||||
except AttributeError: # tobytes() is not available in python2
|
||||
res = ecmd.tostring()
|
||||
_, size, perm_addr = struct.unpack("II%is" % MAX_ADDR_LEN, res)
|
||||
perm_addr = Util.mac_ntoa(perm_addr[:size])
|
||||
except IOError:
|
||||
perm_addr = None
|
||||
finally:
|
||||
sock.close()
|
||||
|
||||
return perm_addr
|
||||
@@ -0,0 +1,7 @@
|
||||
# Relative import is not support by ansible 2.8 yet
|
||||
# pylint: disable=import-error, no-name-in-module
|
||||
from ansible.module_utils.network_lsr.nm import provider # noqa:E501
|
||||
|
||||
# pylint: enable=import-error, no-name-in-module
|
||||
|
||||
provider.NetworkManagerProvider
|
||||
@@ -0,0 +1,128 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
# Handle NM.ActiveConnection
|
||||
|
||||
import logging
|
||||
|
||||
# Relative import is not support by ansible 2.8 yet
|
||||
# pylint: disable=import-error, no-name-in-module
|
||||
from ansible.module_utils.network_lsr.nm import client # noqa:E501
|
||||
from ansible.module_utils.network_lsr.nm import error # noqa:E501
|
||||
|
||||
# pylint: enable=import-error, no-name-in-module
|
||||
|
||||
|
||||
NM_AC_STATE_CHANGED_SIGNAL = "state-changed"
|
||||
|
||||
|
||||
def deactivate_active_connection(nm_ac, timeout, check_mode):
|
||||
if not nm_ac or nm_ac.props.state == client.NM.ActiveConnectionState.DEACTIVATED:
|
||||
logging.info("Connection is not active, no need to deactivate")
|
||||
return False
|
||||
if not check_mode:
|
||||
main_loop = client.get_mainloop(timeout)
|
||||
logging.debug(
|
||||
"Deactivating {id} with timeout {timeout}".format(
|
||||
id=nm_ac.get_id(), timeout=timeout
|
||||
)
|
||||
)
|
||||
user_data = main_loop
|
||||
handler_id = nm_ac.connect(
|
||||
NM_AC_STATE_CHANGED_SIGNAL, _nm_ac_state_change_callback, user_data
|
||||
)
|
||||
logging.debug(
|
||||
"Registered {signal} on client.NM.ActiveConnection {id}".format(
|
||||
signal=NM_AC_STATE_CHANGED_SIGNAL, id=nm_ac.get_id()
|
||||
)
|
||||
)
|
||||
if nm_ac.props.state != client.NM.ActiveConnectionState.DEACTIVATING:
|
||||
nm_client = client.get_client()
|
||||
user_data = (main_loop, nm_ac, nm_ac.get_id(), handler_id)
|
||||
nm_client.deactivate_connection_async(
|
||||
nm_ac,
|
||||
main_loop.cancellable,
|
||||
_nm_ac_deactivate_call_back,
|
||||
user_data,
|
||||
)
|
||||
logging.debug(
|
||||
"Deactivating client.NM.ActiveConnection {0}".format(nm_ac.get_id())
|
||||
)
|
||||
main_loop.run()
|
||||
return True
|
||||
|
||||
|
||||
def _nm_ac_state_change_callback(nm_ac, state, reason, user_data):
|
||||
main_loop = user_data
|
||||
if main_loop.is_cancelled:
|
||||
return
|
||||
logging.debug(
|
||||
"Got client.NM.ActiveConnection state change: {id}: {state} {reason}".format(
|
||||
id=nm_ac.get_id(), state=state, reason=reason
|
||||
)
|
||||
)
|
||||
if nm_ac.props.state == client.NM.ActiveConnectionState.DEACTIVATED:
|
||||
logging.debug(
|
||||
"client.NM.ActiveConnection {0} is deactivated".format(nm_ac.get_id())
|
||||
)
|
||||
main_loop.quit()
|
||||
|
||||
|
||||
def _nm_ac_deactivate_call_back(nm_client, result, user_data):
|
||||
main_loop, nm_ac, nm_ac_id, handler_id = user_data
|
||||
logging.debug("client.NM.ActiveConnection deactivating callback")
|
||||
if main_loop.is_cancelled:
|
||||
if nm_ac:
|
||||
nm_ac.handler_disconnect(handler_id)
|
||||
return
|
||||
|
||||
try:
|
||||
success = nm_client.deactivate_connection_finish(result)
|
||||
except client.GLib.Error as e:
|
||||
if e.matches(
|
||||
client.NM.ManagerError.quark(), client.NM.ManagerError.CONNECTIONNOTACTIVE
|
||||
):
|
||||
logging.info(
|
||||
"Connection is not active on {0}, no need to deactivate".format(
|
||||
nm_ac_id
|
||||
)
|
||||
)
|
||||
if nm_ac:
|
||||
nm_ac.handler_disconnect(handler_id)
|
||||
main_loop.quit()
|
||||
return
|
||||
else:
|
||||
_deactivate_fail(
|
||||
main_loop,
|
||||
handler_id,
|
||||
nm_ac,
|
||||
"Failed to deactivate connection {id}, error={error}".format(
|
||||
id=nm_ac_id, error=e
|
||||
),
|
||||
)
|
||||
return
|
||||
except Exception as e:
|
||||
_deactivate_fail(
|
||||
main_loop,
|
||||
handler_id,
|
||||
nm_ac,
|
||||
"Failed to deactivate connection {id}, error={error}".format(
|
||||
id=nm_ac_id, error=e
|
||||
),
|
||||
)
|
||||
return
|
||||
|
||||
if not success:
|
||||
_deactivate_fail(
|
||||
main_loop,
|
||||
handler_id,
|
||||
nm_ac,
|
||||
"Failed to deactivate connection {0}, error='None "
|
||||
"returned from deactivate_connection_finish()'".format(nm_ac_id),
|
||||
)
|
||||
|
||||
|
||||
def _deactivate_fail(main_loop, handler_id, nm_ac, msg):
|
||||
if nm_ac:
|
||||
nm_ac.handler_disconnect(handler_id)
|
||||
logging.error(msg)
|
||||
main_loop.fail(error.LsrNetworkNmError(msg))
|
||||
@@ -0,0 +1,96 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import logging
|
||||
|
||||
# Relative import is not support by ansible 2.8 yet
|
||||
# pylint: disable=import-error, no-name-in-module
|
||||
from ansible.module_utils.network_lsr.nm import error # noqa:E501
|
||||
|
||||
import gi
|
||||
|
||||
try:
|
||||
gi.require_version("NM", "1.0")
|
||||
|
||||
# It is required to state the NM version before importing it
|
||||
# But this break the flake8 rule: https://www.flake8rules.com/rules/E402.html
|
||||
# Use NOQA: E402 to suppress it.
|
||||
from gi.repository import NM # NOQA: E402
|
||||
from gi.repository import GLib # NOQA: E402
|
||||
from gi.repository import Gio # NOQA: E402
|
||||
|
||||
# pylint: enable=import-error, no-name-in-module
|
||||
|
||||
NM
|
||||
GLib
|
||||
Gio
|
||||
except ValueError:
|
||||
# This is to workaround a bug in ansible 2.9 which causes
|
||||
# this code to be executed on the control node, where NM
|
||||
# is not guaranteed to exist. On the other hand, it is
|
||||
# ensured on the managed nodes as NM package is installed
|
||||
# in the network role. Therefore, this exception handling
|
||||
# does not affect the network installation and configuration
|
||||
# on the managed nodes.
|
||||
pass
|
||||
|
||||
|
||||
def get_client():
|
||||
return NM.Client.new()
|
||||
|
||||
|
||||
class _NmMainLoop(object):
|
||||
def __init__(self, timeout):
|
||||
self._mainloop = GLib.MainLoop()
|
||||
self._cancellable = Gio.Cancellable.new()
|
||||
self._timeout = timeout
|
||||
self._timeout_id = None
|
||||
|
||||
def run(self):
|
||||
logging.debug("NM mainloop running")
|
||||
user_data = None
|
||||
self._timeout_id = GLib.timeout_add(
|
||||
int(self._timeout * 1000),
|
||||
self._timeout_call_back,
|
||||
user_data,
|
||||
)
|
||||
logging.debug("Added timeout checker")
|
||||
self._mainloop.run()
|
||||
|
||||
def _timeout_call_back(self, _user_data):
|
||||
logging.error("Timeout")
|
||||
self.fail(error.LsrNetworkNmError("Timeout"))
|
||||
|
||||
@property
|
||||
def cancellable(self):
|
||||
return self._cancellable
|
||||
|
||||
@property
|
||||
def is_cancelled(self):
|
||||
if self._cancellable:
|
||||
return self._cancellable.is_cancelled()
|
||||
return True
|
||||
|
||||
def _clean_up(self):
|
||||
logging.debug("NM mainloop cleaning up")
|
||||
if self._timeout_id:
|
||||
logging.debug("Removing timeout checker")
|
||||
GLib.source_remove(self._timeout_id)
|
||||
self._timeout_id = None
|
||||
if self._cancellable:
|
||||
logging.debug("Canceling all pending tasks")
|
||||
self._cancellable.cancel()
|
||||
self._cancellable = None
|
||||
self._mainloop = None
|
||||
|
||||
def quit(self):
|
||||
logging.debug("NM mainloop quiting")
|
||||
self._mainloop.quit()
|
||||
self._clean_up()
|
||||
|
||||
def fail(self, exception):
|
||||
self.quit()
|
||||
raise exception
|
||||
|
||||
|
||||
def get_mainloop(timeout):
|
||||
return _NmMainLoop(timeout)
|
||||
@@ -0,0 +1,113 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
# Handle NM.RemoteConnection
|
||||
|
||||
import logging
|
||||
|
||||
# Relative import is not support by ansible 2.8 yet
|
||||
# pylint: disable=import-error, no-name-in-module
|
||||
from ansible.module_utils.network_lsr.nm import client # noqa:E501
|
||||
from ansible.module_utils.network_lsr.nm import error # noqa:E501
|
||||
|
||||
# pylint: enable=import-error, no-name-in-module
|
||||
|
||||
|
||||
def delete_remote_connection(nm_profile, timeout, check_mode):
|
||||
if not nm_profile:
|
||||
logging.info("NULL NM.RemoteConnection, no need to delete")
|
||||
return False
|
||||
|
||||
if not check_mode:
|
||||
main_loop = client.get_mainloop(timeout)
|
||||
user_data = main_loop
|
||||
nm_profile.delete_async(
|
||||
main_loop.cancellable,
|
||||
_nm_profile_delete_call_back,
|
||||
user_data,
|
||||
)
|
||||
logging.debug(
|
||||
"Deleting profile {id}/{uuid} with timeout {timeout}".format(
|
||||
id=nm_profile.get_id(), uuid=nm_profile.get_uuid(), timeout=timeout
|
||||
)
|
||||
)
|
||||
main_loop.run()
|
||||
return True
|
||||
|
||||
|
||||
def _nm_profile_delete_call_back(nm_profile, result, user_data):
|
||||
main_loop = user_data
|
||||
if main_loop.is_cancelled:
|
||||
return
|
||||
|
||||
try:
|
||||
success = nm_profile.delete_finish(result)
|
||||
except Exception as e:
|
||||
main_loop.fail(
|
||||
error.LsrNetworkNmError(
|
||||
"Connection deletion aborted on {id}/{uuid}: error={error}".format(
|
||||
id=nm_profile.get_id(), uuid=nm_profile.get_uuid(), error=e
|
||||
)
|
||||
)
|
||||
)
|
||||
if success:
|
||||
main_loop.quit()
|
||||
else:
|
||||
main_loop.fail(
|
||||
error.LsrNetworkNmError(
|
||||
"Connection deletion aborted on {id}/{uuid}: error=unknown".format(
|
||||
id=nm_profile.get_id(), uuid=nm_profile.get_uuid()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def volatilize_remote_connection(nm_profile, timeout, check_mode):
|
||||
if not nm_profile:
|
||||
logging.info("NULL NM.RemoteConnection, no need to volatilize")
|
||||
return False
|
||||
if not check_mode:
|
||||
main_loop = client.get_mainloop(timeout)
|
||||
user_data = main_loop
|
||||
nm_profile.update2(
|
||||
None, # settings
|
||||
client.NM.SettingsUpdate2Flags.IN_MEMORY_ONLY
|
||||
| client.NM.SettingsUpdate2Flags.VOLATILE,
|
||||
None, # args
|
||||
main_loop.cancellable,
|
||||
_nm_profile_volatile_update2_call_back,
|
||||
user_data,
|
||||
)
|
||||
logging.debug(
|
||||
"Volatilizing profile {id}/{uuid} with timeout {timeout}".format(
|
||||
id=nm_profile.get_id(), uuid=nm_profile.get_uuid(), timeout=timeout
|
||||
)
|
||||
)
|
||||
main_loop.run()
|
||||
return True
|
||||
|
||||
|
||||
def _nm_profile_volatile_update2_call_back(nm_profile, result, user_data):
|
||||
main_loop = user_data
|
||||
if main_loop.is_cancelled:
|
||||
return
|
||||
|
||||
try:
|
||||
success = nm_profile.update2_finish(result)
|
||||
except Exception as e:
|
||||
main_loop.fail(
|
||||
error.LsrNetworkNmError(
|
||||
"Connection volatilize aborted on {id}/{uuid}: error={error}".format(
|
||||
id=nm_profile.get_id(), uuid=nm_profile.get_uuid(), error=e
|
||||
)
|
||||
)
|
||||
)
|
||||
if success:
|
||||
main_loop.quit()
|
||||
else:
|
||||
main_loop.fail(
|
||||
error.LsrNetworkNmError(
|
||||
"Connection volatilize aborted on {id}/{uuid}: error=unknown".format(
|
||||
id=nm_profile.get_id(), uuid=nm_profile.get_uuid()
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
|
||||
class LsrNetworkNmError(Exception):
|
||||
pass
|
||||
@@ -0,0 +1,58 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import logging
|
||||
|
||||
# Relative import is not support by ansible 2.8 yet
|
||||
# pylint: disable=import-error, no-name-in-module
|
||||
from ansible.module_utils.network_lsr.nm import active_connection # noqa:E501
|
||||
from ansible.module_utils.network_lsr.nm import client # noqa:E501
|
||||
from ansible.module_utils.network_lsr.nm import connection # noqa:E501
|
||||
|
||||
# pylint: enable=import-error, no-name-in-module
|
||||
|
||||
|
||||
class NetworkManagerProvider:
|
||||
def deactivate_connection(self, connection_name, timeout, check_mode):
|
||||
"""
|
||||
Return True if changed.
|
||||
"""
|
||||
nm_client = client.get_client()
|
||||
changed = False
|
||||
for nm_ac in nm_client.get_active_connections():
|
||||
nm_profile = nm_ac.get_connection()
|
||||
if nm_profile and nm_profile.get_id() == connection_name:
|
||||
changed |= active_connection.deactivate_active_connection(
|
||||
nm_ac, timeout, check_mode
|
||||
)
|
||||
if not changed:
|
||||
logging.info("No active connection for {0}".format(connection_name))
|
||||
|
||||
return changed
|
||||
|
||||
def volatilize_connection_by_uuid(self, uuid, timeout, check_mode):
|
||||
"""
|
||||
Mark NM.RemoteConnection as volatile(delete on deactivation) via Update2,
|
||||
if not supported, delete the profile.
|
||||
|
||||
Return True if changed.
|
||||
"""
|
||||
nm_client = client.get_client()
|
||||
changed = False
|
||||
for nm_profile in nm_client.get_connections():
|
||||
if nm_profile and nm_profile.get_uuid() == uuid:
|
||||
if hasattr(nm_profile, "update2"):
|
||||
changed |= connection.volatilize_remote_connection(
|
||||
nm_profile, timeout, check_mode
|
||||
)
|
||||
else:
|
||||
changed |= connection.delete_remote_connection(
|
||||
nm_profile, timeout, check_mode
|
||||
)
|
||||
if not changed:
|
||||
logging.info("No connection with UUID {0} to volatilize".format(uuid))
|
||||
|
||||
return changed
|
||||
|
||||
def get_connections(self):
|
||||
nm_client = client.get_client()
|
||||
return nm_client.get_connections()
|
||||
Reference in New Issue
Block a user