mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
aa6b5e4a60
They are useful only with RHN, which Red Hat discontinued many years ago, and with Spacewalk 5, which is EOL for more than 3 years; while they could be potentially used with Uyuni / SUSE Manager (fork of Spacewalk 5), we have not heard about anyone using it in those setups. Hence, deprecate these two modules, with their removal planned for 10.0.0 in case there are no reports about being still useful, and potentially noone that steps up to maintain them.
465 lines
16 KiB
Python
465 lines
16 KiB
Python
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright (c) James Laska
|
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
__metaclass__ = type
|
|
|
|
DOCUMENTATION = r'''
|
|
---
|
|
module: rhn_register
|
|
short_description: Manage Red Hat Network registration using the C(rhnreg_ks) command
|
|
description:
|
|
- Manage registration to the Red Hat Network.
|
|
author:
|
|
- James Laska (@jlaska)
|
|
notes:
|
|
- This is for older Red Hat products. You probably want the M(community.general.redhat_subscription) module instead.
|
|
- In order to register a system, C(rhnreg_ks) requires either a username and password, or an activationkey.
|
|
requirements:
|
|
- rhnreg_ks
|
|
- either libxml2 or lxml
|
|
extends_documentation_fragment:
|
|
- community.general.attributes
|
|
attributes:
|
|
check_mode:
|
|
support: none
|
|
diff_mode:
|
|
support: none
|
|
options:
|
|
state:
|
|
description:
|
|
- Whether to register (C(present)), or unregister (C(absent)) a system.
|
|
type: str
|
|
choices: [ absent, present ]
|
|
default: present
|
|
username:
|
|
description:
|
|
- Red Hat Network username.
|
|
type: str
|
|
password:
|
|
description:
|
|
- Red Hat Network password.
|
|
type: str
|
|
server_url:
|
|
description:
|
|
- Specify an alternative Red Hat Network server URL.
|
|
- The default is the current value of I(serverURL) from C(/etc/sysconfig/rhn/up2date).
|
|
type: str
|
|
activationkey:
|
|
description:
|
|
- Supply an activation key for use with registration.
|
|
type: str
|
|
profilename:
|
|
description:
|
|
- Supply an profilename for use with registration.
|
|
type: str
|
|
force:
|
|
description:
|
|
- Force registration, even if system is already registered.
|
|
type: bool
|
|
default: false
|
|
version_added: 2.0.0
|
|
ca_cert:
|
|
description:
|
|
- Supply a custom ssl CA certificate file for use with registration.
|
|
type: path
|
|
aliases: [ sslcacert ]
|
|
systemorgid:
|
|
description:
|
|
- Supply an organizational id for use with registration.
|
|
type: str
|
|
channels:
|
|
description:
|
|
- Optionally specify a list of channels to subscribe to upon successful registration.
|
|
type: list
|
|
elements: str
|
|
default: []
|
|
enable_eus:
|
|
description:
|
|
- If C(false), extended update support will be requested.
|
|
type: bool
|
|
default: false
|
|
nopackages:
|
|
description:
|
|
- If C(true), the registered node will not upload its installed packages information to Satellite server.
|
|
type: bool
|
|
default: false
|
|
deprecated:
|
|
removed_in: 10.0.0
|
|
why: |
|
|
RHN hosted at redhat.com was discontinued years ago, and Spacewalk 5
|
|
(which uses RHN) is EOL since 2020, May 31st; while this module could
|
|
work on Uyuni / SUSE Manager (fork of Spacewalk 5), we have not heard
|
|
about anyone using it in those setups.
|
|
alternative: |
|
|
Contact the community.general maintainers to report the usage of this
|
|
module, and potentially step up to maintain it.
|
|
'''
|
|
|
|
EXAMPLES = r'''
|
|
- name: Unregister system from RHN
|
|
community.general.rhn_register:
|
|
state: absent
|
|
username: joe_user
|
|
password: somepass
|
|
|
|
- name: Register as user with password and auto-subscribe to available content
|
|
community.general.rhn_register:
|
|
state: present
|
|
username: joe_user
|
|
password: somepass
|
|
|
|
- name: Register with activationkey and enable extended update support
|
|
community.general.rhn_register:
|
|
state: present
|
|
activationkey: 1-222333444
|
|
enable_eus: true
|
|
|
|
- name: Register with activationkey and set a profilename which may differ from the hostname
|
|
community.general.rhn_register:
|
|
state: present
|
|
activationkey: 1-222333444
|
|
profilename: host.example.com.custom
|
|
|
|
- name: Register as user with password against a satellite server
|
|
community.general.rhn_register:
|
|
state: present
|
|
username: joe_user
|
|
password: somepass
|
|
server_url: https://xmlrpc.my.satellite/XMLRPC
|
|
|
|
- name: Register as user with password and enable channels
|
|
community.general.rhn_register:
|
|
state: present
|
|
username: joe_user
|
|
password: somepass
|
|
channels: rhel-x86_64-server-6-foo-1,rhel-x86_64-server-6-bar-1
|
|
|
|
- name: Force-register as user with password to ensure registration is current on server
|
|
community.general.rhn_register:
|
|
state: present
|
|
username: joe_user
|
|
password: somepass
|
|
server_url: https://xmlrpc.my.satellite/XMLRPC
|
|
force: true
|
|
'''
|
|
|
|
RETURN = r'''
|
|
# Default return values
|
|
'''
|
|
|
|
import os
|
|
import sys
|
|
|
|
# Attempt to import rhn client tools
|
|
sys.path.insert(0, '/usr/share/rhn')
|
|
try:
|
|
import up2date_client
|
|
import up2date_client.config
|
|
HAS_UP2DATE_CLIENT = True
|
|
except ImportError:
|
|
HAS_UP2DATE_CLIENT = False
|
|
|
|
# INSERT REDHAT SNIPPETS
|
|
from ansible_collections.community.general.plugins.module_utils import redhat
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
from ansible.module_utils.six.moves import urllib, xmlrpc_client
|
|
|
|
|
|
class Rhn(redhat.RegistrationBase):
|
|
|
|
def __init__(self, module=None, username=None, password=None):
|
|
redhat.RegistrationBase.__init__(self, module, username, password)
|
|
self.config = self.load_config()
|
|
self.server = None
|
|
self.session = None
|
|
|
|
def logout(self):
|
|
if self.session is not None:
|
|
self.server.auth.logout(self.session)
|
|
|
|
def load_config(self):
|
|
'''
|
|
Read configuration from /etc/sysconfig/rhn/up2date
|
|
'''
|
|
if not HAS_UP2DATE_CLIENT:
|
|
return None
|
|
|
|
config = up2date_client.config.initUp2dateConfig()
|
|
|
|
return config
|
|
|
|
@property
|
|
def server_url(self):
|
|
return self.config['serverURL']
|
|
|
|
@property
|
|
def hostname(self):
|
|
'''
|
|
Return the non-xmlrpc RHN hostname. This is a convenience method
|
|
used for displaying a more readable RHN hostname.
|
|
|
|
Returns: str
|
|
'''
|
|
url = urllib.parse.urlparse(self.server_url)
|
|
return url[1].replace('xmlrpc.', '')
|
|
|
|
@property
|
|
def systemid(self):
|
|
systemid = None
|
|
xpath_str = "//member[name='system_id']/value/string"
|
|
|
|
if os.path.isfile(self.config['systemIdPath']):
|
|
fd = open(self.config['systemIdPath'], 'r')
|
|
xml_data = fd.read()
|
|
fd.close()
|
|
|
|
# Ugh, xml parsing time ...
|
|
# First, try parsing with libxml2 ...
|
|
if systemid is None:
|
|
try:
|
|
import libxml2
|
|
doc = libxml2.parseDoc(xml_data)
|
|
ctxt = doc.xpathNewContext()
|
|
systemid = ctxt.xpathEval(xpath_str)[0].content
|
|
doc.freeDoc()
|
|
ctxt.xpathFreeContext()
|
|
except ImportError:
|
|
pass
|
|
|
|
# m-kay, let's try with lxml now ...
|
|
if systemid is None:
|
|
try:
|
|
from lxml import etree
|
|
root = etree.fromstring(xml_data)
|
|
systemid = root.xpath(xpath_str)[0].text
|
|
except ImportError:
|
|
raise Exception('"libxml2" or "lxml" is required for this module.')
|
|
|
|
# Strip the 'ID-' prefix
|
|
if systemid is not None and systemid.startswith('ID-'):
|
|
systemid = systemid[3:]
|
|
|
|
return int(systemid)
|
|
|
|
@property
|
|
def is_registered(self):
|
|
'''
|
|
Determine whether the current system is registered.
|
|
|
|
Returns: True|False
|
|
'''
|
|
return os.path.isfile(self.config['systemIdPath'])
|
|
|
|
def configure_server_url(self, server_url):
|
|
'''
|
|
Configure server_url for registration
|
|
'''
|
|
|
|
self.config.set('serverURL', server_url)
|
|
self.config.save()
|
|
|
|
def enable(self):
|
|
'''
|
|
Prepare the system for RHN registration. This includes ...
|
|
* enabling the rhnplugin yum plugin
|
|
* disabling the subscription-manager yum plugin
|
|
'''
|
|
redhat.RegistrationBase.enable(self)
|
|
self.update_plugin_conf('rhnplugin', True)
|
|
self.update_plugin_conf('subscription-manager', False)
|
|
|
|
def register(self, enable_eus=False, activationkey=None, profilename=None, sslcacert=None, systemorgid=None, nopackages=False):
|
|
'''
|
|
Register system to RHN. If enable_eus=True, extended update
|
|
support will be requested.
|
|
'''
|
|
register_cmd = ['/usr/sbin/rhnreg_ks', '--force']
|
|
if self.username:
|
|
register_cmd.extend(['--username', self.username, '--password', self.password])
|
|
if self.server_url:
|
|
register_cmd.extend(['--serverUrl', self.server_url])
|
|
if enable_eus:
|
|
register_cmd.append('--use-eus-channel')
|
|
if nopackages:
|
|
register_cmd.append('--nopackages')
|
|
if activationkey is not None:
|
|
register_cmd.extend(['--activationkey', activationkey])
|
|
if profilename is not None:
|
|
register_cmd.extend(['--profilename', profilename])
|
|
if sslcacert is not None:
|
|
register_cmd.extend(['--sslCACert', sslcacert])
|
|
if systemorgid is not None:
|
|
register_cmd.extend(['--systemorgid', systemorgid])
|
|
rc, stdout, stderr = self.module.run_command(register_cmd, check_rc=True)
|
|
|
|
def api(self, method, *args):
|
|
'''
|
|
Convenience RPC wrapper
|
|
'''
|
|
if self.server is None:
|
|
if self.hostname != 'rhn.redhat.com':
|
|
url = "https://%s/rpc/api" % self.hostname
|
|
else:
|
|
url = "https://xmlrpc.%s/rpc/api" % self.hostname
|
|
self.server = xmlrpc_client.ServerProxy(url)
|
|
self.session = self.server.auth.login(self.username, self.password)
|
|
|
|
func = getattr(self.server, method)
|
|
return func(self.session, *args)
|
|
|
|
def unregister(self):
|
|
'''
|
|
Unregister a previously registered system
|
|
'''
|
|
|
|
# Initiate RPC connection
|
|
self.api('system.deleteSystems', [self.systemid])
|
|
|
|
# Remove systemid file
|
|
os.unlink(self.config['systemIdPath'])
|
|
|
|
def subscribe(self, channels):
|
|
if not channels:
|
|
return
|
|
|
|
if self._is_hosted():
|
|
current_channels = self.api('channel.software.listSystemChannels', self.systemid)
|
|
new_channels = [item['channel_label'] for item in current_channels]
|
|
new_channels.extend(channels)
|
|
return self.api('channel.software.setSystemChannels', self.systemid, list(new_channels))
|
|
|
|
else:
|
|
current_channels = self.api('channel.software.listSystemChannels', self.systemid)
|
|
current_channels = [item['label'] for item in current_channels]
|
|
new_base = None
|
|
new_childs = []
|
|
for ch in channels:
|
|
if ch in current_channels:
|
|
continue
|
|
if self.api('channel.software.getDetails', ch)['parent_channel_label'] == '':
|
|
new_base = ch
|
|
else:
|
|
if ch not in new_childs:
|
|
new_childs.append(ch)
|
|
out_base = 0
|
|
out_childs = 0
|
|
|
|
if new_base:
|
|
out_base = self.api('system.setBaseChannel', self.systemid, new_base)
|
|
|
|
if new_childs:
|
|
out_childs = self.api('system.setChildChannels', self.systemid, new_childs)
|
|
|
|
return out_base and out_childs
|
|
|
|
def _is_hosted(self):
|
|
'''
|
|
Return True if we are running against Hosted (rhn.redhat.com) or
|
|
False otherwise (when running against Satellite or Spacewalk)
|
|
'''
|
|
return 'rhn.redhat.com' in self.hostname
|
|
|
|
|
|
def main():
|
|
|
|
module = AnsibleModule(
|
|
argument_spec=dict(
|
|
state=dict(type='str', default='present', choices=['absent', 'present']),
|
|
username=dict(type='str'),
|
|
password=dict(type='str', no_log=True),
|
|
server_url=dict(type='str'),
|
|
activationkey=dict(type='str', no_log=True),
|
|
profilename=dict(type='str'),
|
|
ca_cert=dict(type='path', aliases=['sslcacert']),
|
|
systemorgid=dict(type='str'),
|
|
enable_eus=dict(type='bool', default=False),
|
|
force=dict(type='bool', default=False),
|
|
nopackages=dict(type='bool', default=False),
|
|
channels=dict(type='list', elements='str', default=[]),
|
|
),
|
|
# username/password is required for state=absent, or if channels is not empty
|
|
# (basically anything that uses self.api requires username/password) but it doesn't
|
|
# look like we can express that with required_if/required_together/mutually_exclusive
|
|
|
|
# only username+password can be used for unregister
|
|
required_if=[['state', 'absent', ['username', 'password']]],
|
|
)
|
|
|
|
if not HAS_UP2DATE_CLIENT:
|
|
module.fail_json(msg="Unable to import up2date_client. Is 'rhn-client-tools' installed?")
|
|
|
|
server_url = module.params['server_url']
|
|
username = module.params['username']
|
|
password = module.params['password']
|
|
|
|
state = module.params['state']
|
|
force = module.params['force']
|
|
activationkey = module.params['activationkey']
|
|
profilename = module.params['profilename']
|
|
sslcacert = module.params['ca_cert']
|
|
systemorgid = module.params['systemorgid']
|
|
channels = module.params['channels']
|
|
enable_eus = module.params['enable_eus']
|
|
nopackages = module.params['nopackages']
|
|
|
|
rhn = Rhn(module=module, username=username, password=password)
|
|
|
|
# use the provided server url and persist it to the rhn config.
|
|
if server_url:
|
|
rhn.configure_server_url(server_url)
|
|
|
|
if not rhn.server_url:
|
|
module.fail_json(
|
|
msg="No serverURL was found (from either the 'server_url' module arg or the config file option 'serverURL' in /etc/sysconfig/rhn/up2date)"
|
|
)
|
|
|
|
# Ensure system is registered
|
|
if state == 'present':
|
|
|
|
# Check for missing parameters ...
|
|
if not (activationkey or rhn.username or rhn.password):
|
|
module.fail_json(msg="Missing arguments, must supply an activationkey (%s) or username (%s) and password (%s)" % (activationkey, rhn.username,
|
|
rhn.password))
|
|
if not activationkey and not (rhn.username and rhn.password):
|
|
module.fail_json(msg="Missing arguments, If registering without an activationkey, must supply username or password")
|
|
|
|
# Register system
|
|
if rhn.is_registered and not force:
|
|
module.exit_json(changed=False, msg="System already registered.")
|
|
|
|
try:
|
|
rhn.enable()
|
|
rhn.register(enable_eus, activationkey, profilename, sslcacert, systemorgid, nopackages)
|
|
rhn.subscribe(channels)
|
|
except Exception as exc:
|
|
module.fail_json(msg="Failed to register with '%s': %s" % (rhn.hostname, exc))
|
|
finally:
|
|
rhn.logout()
|
|
|
|
module.exit_json(changed=True, msg="System successfully registered to '%s'." % rhn.hostname)
|
|
|
|
# Ensure system is *not* registered
|
|
if state == 'absent':
|
|
if not rhn.is_registered:
|
|
module.exit_json(changed=False, msg="System already unregistered.")
|
|
|
|
if not (rhn.username and rhn.password):
|
|
module.fail_json(msg="Missing arguments, the system is currently registered and unregistration requires a username and password")
|
|
|
|
try:
|
|
rhn.unregister()
|
|
except Exception as exc:
|
|
module.fail_json(msg="Failed to unregister: %s" % exc)
|
|
finally:
|
|
rhn.logout()
|
|
|
|
module.exit_json(changed=True, msg="System successfully unregistered from %s." % rhn.hostname)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|