Build Windows Templates in RHV

This commit is contained in:
2021-05-03 13:47:44 -04:00
parent 595021d449
commit 28c9375b0d
290 changed files with 10931 additions and 159 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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))

View File

@@ -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)

View File

@@ -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()
)
)
)

View File

@@ -0,0 +1,5 @@
# SPDX-License-Identifier: BSD-3-Clause
class LsrNetworkNmError(Exception):
pass

View File

@@ -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()