2013-06-06 20:54:35 +02:00
#!/usr/bin/python
2016-03-31 20:17:40 +02:00
# James Laska (jlaska@redhat.com)
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
2013-06-06 20:54:35 +02:00
DOCUMENTATION = '''
- - -
2013-06-08 20:18:31 +02:00
module : redhat_subscription
2013-06-06 22:30:06 +02:00
short_description : Manage Red Hat Network registration and subscriptions using the C ( subscription - manager ) command
2013-06-06 20:54:35 +02:00
description :
2013-06-06 22:30:06 +02:00
- Manage registration and subscription to the Red Hat Network entitlement platform .
2013-06-07 01:21:18 +02:00
version_added : " 1.2 "
2015-09-22 11:51:02 +02:00
author : " Barnaby Court (@barnabycourt) "
2013-06-06 20:54:35 +02:00
notes :
2013-06-07 01:21:18 +02:00
- In order to register a system , subscription - manager requires either a username and password , or an activationkey .
2013-06-06 20:54:35 +02:00
requirements :
2013-06-06 22:30:06 +02:00
- subscription - manager
2013-06-06 20:54:35 +02:00
options :
2013-06-06 22:30:06 +02:00
state :
description :
- whether to register and subscribe ( C ( present ) ) , or unregister ( C ( absent ) ) a system
required : false
choices : [ " present " , " absent " ]
default : " present "
2013-06-06 20:54:35 +02:00
username :
2013-06-07 01:21:18 +02:00
description :
2013-06-06 22:30:06 +02:00
- Red Hat Network username
2013-06-07 01:21:18 +02:00
required : False
2013-06-06 20:54:35 +02:00
default : null
password :
description :
2013-06-06 22:30:06 +02:00
- Red Hat Network password
2013-06-07 01:21:18 +02:00
required : False
2013-06-06 20:54:35 +02:00
default : null
server_hostname :
description :
2013-06-06 22:30:06 +02:00
- Specify an alternative Red Hat Network server
2013-06-07 01:21:18 +02:00
required : False
2013-06-06 22:30:06 +02:00
default : Current value from C ( / etc / rhsm / rhsm . conf ) is the default
2013-06-06 20:54:35 +02:00
server_insecure :
description :
2016-04-12 17:51:35 +02:00
- Enable or disable https server certificate verification when connecting to C ( server_hostname )
2013-06-07 01:21:18 +02:00
required : False
2013-06-06 22:30:06 +02:00
default : Current value from C ( / etc / rhsm / rhsm . conf ) is the default
2013-06-06 20:54:35 +02:00
rhsm_baseurl :
description :
- Specify CDN baseurl
2013-06-07 01:21:18 +02:00
required : False
2013-06-06 22:30:06 +02:00
default : Current value from C ( / etc / rhsm / rhsm . conf ) is the default
2013-06-06 20:54:35 +02:00
autosubscribe :
description :
2013-06-07 01:21:18 +02:00
- Upon successful registration , auto - consume available subscriptions
required : False
default : False
2013-06-06 20:54:35 +02:00
activationkey :
description :
- supply an activation key for use with registration
2013-06-07 01:21:18 +02:00
required : False
2013-06-06 20:54:35 +02:00
default : null
2015-03-01 14:16:44 +01:00
org_id :
description :
- Organisation ID to use in conjunction with activationkey
required : False
default : null
2015-04-14 20:56:55 +02:00
version_added : " 2.0 "
2013-06-06 20:54:35 +02:00
pool :
description :
2013-06-07 01:21:18 +02:00
- Specify a subscription pool name to consume . Regular expressions accepted .
required : False
2013-06-06 20:54:35 +02:00
default : ' ^$ '
2016-04-20 17:33:08 +02:00
consumer_type :
description :
- The type of unit to register , defaults to system
required : False
default : null
version_added : " 2.1 "
consumer_name :
description :
- Name of the system to register , defaults to the hostname
required : False
default : null
version_added : " 2.1 "
consumer_id :
description :
- References an existing consumer ID to resume using a previous registration for this system . If the system ' s identity certificate is lost or corrupted, this option allows it to resume using its previous identity and subscriptions. The default is to not specify a consumer ID so a new ID is created.
required : False
default : null
version_added : " 2.1 "
2013-06-14 11:53:43 +02:00
'''
EXAMPLES = '''
# Register as user (joe_user) with password (somepass) and auto-subscribe to available content.
2014-12-05 02:31:35 +01:00
- redhat_subscription : state = present username = joe_user password = somepass autosubscribe = true
2013-06-14 11:53:43 +02:00
2016-04-20 17:33:08 +02:00
# Same as above but with pulling existing system data.
- redhat_subscription : state = present username = joe_user password = somepass
consumer_id = xxxxxxxx - xxxx - xxxx - xxxx - xxxxxxxxxxxx
2013-06-14 11:53:43 +02:00
# Register with activationkey (1-222333444) and consume subscriptions matching
# the names (Red hat Enterprise Server) and (Red Hat Virtualization)
2014-12-05 02:31:35 +01:00
- redhat_subscription : state = present
2013-06-14 11:53:43 +02:00
activationkey = 1 - 222333444
pool = ' ^(Red Hat Enterprise Server|Red Hat Virtualization)$ '
2015-07-28 12:23:20 +02:00
# Update the consumed subscriptions from the previous example (remove the Red
# Hat Virtualization subscription)
- redhat_subscription : state = present
activationkey = 1 - 222333444
pool = ' ^Red Hat Enterprise Server$ '
2013-06-06 20:54:35 +02:00
'''
import os
import re
import types
import ConfigParser
import shlex
2014-03-10 22:11:24 +01:00
class RegistrationBase ( object ) :
def __init__ ( self , module , username = None , password = None ) :
self . module = module
2013-06-07 20:07:00 +02:00
self . username = username
self . password = password
def configure ( self ) :
raise NotImplementedError ( " Must be implemented by a sub-class " )
def enable ( self ) :
# Remove any existing redhat.repo
redhat_repo = ' /etc/yum.repos.d/redhat.repo '
if os . path . isfile ( redhat_repo ) :
os . unlink ( redhat_repo )
def register ( self ) :
raise NotImplementedError ( " Must be implemented by a sub-class " )
def unregister ( self ) :
raise NotImplementedError ( " Must be implemented by a sub-class " )
def unsubscribe ( self ) :
raise NotImplementedError ( " Must be implemented by a sub-class " )
def update_plugin_conf ( self , plugin , enabled = True ) :
plugin_conf = ' /etc/yum/pluginconf.d/ %s .conf ' % plugin
if os . path . isfile ( plugin_conf ) :
cfg = ConfigParser . ConfigParser ( )
cfg . read ( [ plugin_conf ] )
if enabled :
cfg . set ( ' main ' , ' enabled ' , 1 )
else :
cfg . set ( ' main ' , ' enabled ' , 0 )
fd = open ( plugin_conf , ' rwa+ ' )
cfg . write ( fd )
fd . close ( )
def subscribe ( self , * * kwargs ) :
raise NotImplementedError ( " Must be implemented by a sub-class " )
class Rhsm ( RegistrationBase ) :
2014-03-10 22:11:24 +01:00
def __init__ ( self , module , username = None , password = None ) :
RegistrationBase . __init__ ( self , module , username , password )
2013-06-07 20:07:00 +02:00
self . config = self . _read_config ( )
2014-03-10 22:11:24 +01:00
self . module = module
2013-06-07 20:07:00 +02:00
2013-06-07 20:15:06 +02:00
def _read_config ( self , rhsm_conf = ' /etc/rhsm/rhsm.conf ' ) :
2013-06-07 20:07:00 +02:00
'''
Load RHSM configuration from / etc / rhsm / rhsm . conf .
Returns :
* ConfigParser object
'''
# Read RHSM defaults ...
cp = ConfigParser . ConfigParser ( )
2013-06-07 20:15:06 +02:00
cp . read ( rhsm_conf )
2013-06-07 20:07:00 +02:00
# Add support for specifying a default value w/o having to standup some configuration
# Yeah, I know this should be subclassed ... but, oh well
def get_option_default ( self , key , default = ' ' ) :
sect , opt = key . split ( ' . ' , 1 )
if self . has_section ( sect ) and self . has_option ( sect , opt ) :
return self . get ( sect , opt )
else :
return default
cp . get_option = types . MethodType ( get_option_default , cp , ConfigParser . ConfigParser )
return cp
def enable ( self ) :
'''
Enable the system to receive updates from subscription - manager .
This involves updating affected yum plugins and removing any
conflicting yum repositories .
'''
RegistrationBase . enable ( self )
self . update_plugin_conf ( ' rhnplugin ' , False )
self . update_plugin_conf ( ' subscription-manager ' , True )
def configure ( self , * * kwargs ) :
'''
Configure the system as directed for registration with RHN
Raises :
* Exception - if error occurs while running command
'''
args = [ ' subscription-manager ' , ' config ' ]
# Pass supplied **kwargs as parameters to subscription-manager. Ignore
# non-configuration parameters and replace '_' with '.'. For example,
# 'server_hostname' becomes '--system.hostname'.
for k , v in kwargs . items ( ) :
if re . search ( r ' ^(system|rhsm)_ ' , k ) :
args . append ( ' -- %s = %s ' % ( k . replace ( ' _ ' , ' . ' ) , v ) )
2015-04-25 06:12:25 +02:00
2014-03-10 22:11:24 +01:00
self . module . run_command ( args , check_rc = True )
2013-06-07 20:07:00 +02:00
@property
def is_registered ( self ) :
'''
Determine whether the current system
Returns :
* Boolean - whether the current system is currently registered to
RHN .
'''
# Quick version...
if False :
return os . path . isfile ( ' /etc/pki/consumer/cert.pem ' ) and \
os . path . isfile ( ' /etc/pki/consumer/key.pem ' )
args = [ ' subscription-manager ' , ' identity ' ]
2014-03-10 22:11:24 +01:00
rc , stdout , stderr = self . module . run_command ( args , check_rc = False )
if rc == 0 :
2013-06-07 20:07:00 +02:00
return True
2014-03-10 22:11:24 +01:00
else :
return False
2013-06-07 20:07:00 +02:00
2016-04-20 17:33:08 +02:00
def register ( self , username , password , autosubscribe , activationkey , org_id ,
consumer_type , consumer_name , consumer_id ) :
2013-06-07 20:07:00 +02:00
'''
Register the current system to the provided RHN server
Raises :
* Exception - if error occurs while running command
'''
args = [ ' subscription-manager ' , ' register ' ]
# Generate command arguments
if activationkey :
2015-04-02 22:16:43 +02:00
args . extend ( [ ' --activationkey ' , activationkey ] )
2015-03-01 14:16:44 +01:00
if org_id :
args . extend ( [ ' --org ' , org_id ] )
2013-06-07 20:07:00 +02:00
else :
if autosubscribe :
args . append ( ' --autosubscribe ' )
if username :
args . extend ( [ ' --username ' , username ] )
if password :
args . extend ( [ ' --password ' , password ] )
2016-04-20 17:33:08 +02:00
if consumer_type :
args . extend ( [ ' --type ' , consumer_type ] )
if consumer_name :
args . extend ( [ ' --name ' , consumer_name ] )
if consumer_id :
args . extend ( [ ' --consumerid ' , consumer_id ] )
2013-06-07 20:07:00 +02:00
2014-03-10 22:11:24 +01:00
rc , stderr , stdout = self . module . run_command ( args , check_rc = True )
2013-06-07 20:07:00 +02:00
2015-04-25 06:12:25 +02:00
def unsubscribe ( self , serials = None ) :
2013-06-07 20:07:00 +02:00
'''
2015-04-25 06:12:25 +02:00
Unsubscribe a system from subscribed channels
Args :
serials ( list or None ) : list of serials to unsubscribe . If
serials is none or an empty list , then
all subscribed channels will be removed .
2013-06-07 20:07:00 +02:00
Raises :
* Exception - if error occurs while running command
'''
2015-04-25 06:12:25 +02:00
items = [ ]
if serials is not None and serials :
items = [ " --serial= %s " % s for s in serials ]
if serials is None :
items = [ " --all " ]
if items :
args = [ ' subscription-manager ' , ' unsubscribe ' ] + items
rc , stderr , stdout = self . module . run_command ( args , check_rc = True )
return serials
2013-06-07 20:07:00 +02:00
def unregister ( self ) :
'''
Unregister a currently registered system
Raises :
* Exception - if error occurs while running command
'''
args = [ ' subscription-manager ' , ' unregister ' ]
2014-03-10 22:11:24 +01:00
rc , stderr , stdout = self . module . run_command ( args , check_rc = True )
2013-06-07 20:07:00 +02:00
def subscribe ( self , regexp ) :
'''
Subscribe current system to available pools matching the specified
regular expression
Raises :
* Exception - if error occurs while running command
'''
# Available pools ready for subscription
2014-03-10 22:11:24 +01:00
available_pools = RhsmPools ( self . module )
2013-06-07 20:07:00 +02:00
2015-04-25 06:12:25 +02:00
subscribed_pool_ids = [ ]
2013-06-07 20:07:00 +02:00
for pool in available_pools . filter ( regexp ) :
pool . subscribe ( )
2015-04-25 06:12:25 +02:00
subscribed_pool_ids . append ( pool . get_pool_id ( ) )
return subscribed_pool_ids
def update_subscriptions ( self , regexp ) :
changed = False
consumed_pools = RhsmPools ( self . module , consumed = True )
pool_ids_to_keep = [ p . get_pool_id ( ) for p in consumed_pools . filter ( regexp ) ]
serials_to_remove = [ p . Serial for p in consumed_pools if p . get_pool_id ( ) not in pool_ids_to_keep ]
serials = self . unsubscribe ( serials = serials_to_remove )
subscribed_pool_ids = self . subscribe ( regexp )
if subscribed_pool_ids or serials :
changed = True
return { ' changed ' : changed , ' subscribed_pool_ids ' : subscribed_pool_ids ,
' unsubscribed_serials ' : serials }
2013-06-07 20:07:00 +02:00
2013-06-06 20:54:35 +02:00
class RhsmPool ( object ) :
2013-06-07 20:07:00 +02:00
'''
Convenience class for housing subscription information
'''
2013-07-01 00:54:32 +02:00
2014-03-10 22:11:24 +01:00
def __init__ ( self , module , * * kwargs ) :
self . module = module
2013-06-06 20:54:35 +02:00
for k , v in kwargs . items ( ) :
setattr ( self , k , v )
2013-07-01 00:54:32 +02:00
2013-06-06 20:54:35 +02:00
def __str__ ( self ) :
return str ( self . __getattribute__ ( ' _name ' ) )
2013-07-01 00:54:32 +02:00
2015-04-25 06:12:25 +02:00
def get_pool_id ( self ) :
return getattr ( self , ' PoolId ' , getattr ( self , ' PoolID ' ) )
2013-06-06 20:54:35 +02:00
def subscribe ( self ) :
2015-04-25 06:12:25 +02:00
args = " subscription-manager subscribe --pool %s " % self . get_pool_id ( )
2014-03-10 22:11:24 +01:00
rc , stdout , stderr = self . module . run_command ( args , check_rc = True )
if rc == 0 :
return True
else :
return False
2013-06-06 20:54:35 +02:00
class RhsmPools ( object ) :
"""
This class is used for manipulating pools subscriptions with RHSM
"""
2015-04-25 06:12:25 +02:00
def __init__ ( self , module , consumed = False ) :
2014-03-10 22:11:24 +01:00
self . module = module
2015-04-25 06:12:25 +02:00
self . products = self . _load_product_list ( consumed )
2013-06-06 20:54:35 +02:00
def __iter__ ( self ) :
return self . products . __iter__ ( )
2015-04-25 06:12:25 +02:00
def _load_product_list ( self , consumed = False ) :
2013-06-06 20:54:35 +02:00
"""
2015-04-25 06:12:25 +02:00
Loads list of all available or consumed pools for system in data structure
Args :
consumed ( bool ) : if True list consumed pools , else list available pools ( default False )
2013-06-06 20:54:35 +02:00
"""
2015-04-25 06:12:25 +02:00
args = " subscription-manager list "
2015-07-28 12:30:37 +02:00
if consumed :
args + = " --consumed "
else :
args + = " --available "
2014-03-10 22:11:24 +01:00
rc , stdout , stderr = self . module . run_command ( args , check_rc = True )
2013-06-06 20:54:35 +02:00
products = [ ]
for line in stdout . split ( ' \n ' ) :
# Remove leading+trailing whitespace
line = line . strip ( )
# An empty line implies the end of a output group
if len ( line ) == 0 :
continue
# If a colon ':' is found, parse
elif ' : ' in line :
( key , value ) = line . split ( ' : ' , 1 )
key = key . strip ( ) . replace ( " " , " " ) # To unify
value = value . strip ( )
if key in [ ' ProductName ' , ' SubscriptionName ' ] :
# Remember the name for later processing
2014-03-10 22:11:24 +01:00
products . append ( RhsmPool ( self . module , _name = value , key = value ) )
2013-06-06 20:54:35 +02:00
elif products :
# Associate value with most recently recorded product
products [ - 1 ] . __setattr__ ( key , value )
# FIXME - log some warning?
#else:
# warnings.warn("Unhandled subscription key/value: %s/%s" % (key,value))
return products
def filter ( self , regexp = ' ^$ ' ) :
'''
Return a list of RhsmPools whose name matches the provided regular expression
'''
r = re . compile ( regexp )
for product in self . products :
if r . search ( product . _name ) :
yield product
def main ( ) :
# Load RHSM configuration from file
2014-04-01 20:22:29 +02:00
rhn = Rhsm ( None )
2013-06-06 20:54:35 +02:00
module = AnsibleModule (
argument_spec = dict (
state = dict ( default = ' present ' , choices = [ ' present ' , ' absent ' ] ) ,
username = dict ( default = None , required = False ) ,
2015-12-23 04:26:51 +01:00
password = dict ( default = None , required = False , no_log = True ) ,
2013-06-07 20:07:00 +02:00
server_hostname = dict ( default = rhn . config . get_option ( ' server.hostname ' ) , required = False ) ,
server_insecure = dict ( default = rhn . config . get_option ( ' server.insecure ' ) , required = False ) ,
rhsm_baseurl = dict ( default = rhn . config . get_option ( ' rhsm.baseurl ' ) , required = False ) ,
2013-06-06 20:54:35 +02:00
autosubscribe = dict ( default = False , type = ' bool ' ) ,
activationkey = dict ( default = None , required = False ) ,
2015-03-01 14:16:44 +01:00
org_id = dict ( default = None , required = False ) ,
2013-06-06 20:54:35 +02:00
pool = dict ( default = ' ^$ ' , required = False , type = ' str ' ) ,
2016-04-20 17:33:08 +02:00
consumer_type = dict ( default = None , required = False ) ,
consumer_name = dict ( default = None , required = False ) ,
consumer_id = dict ( default = None , required = False ) ,
2013-06-06 20:54:35 +02:00
)
)
2014-03-10 22:11:24 +01:00
rhn . module = module
2013-06-06 20:54:35 +02:00
state = module . params [ ' state ' ]
username = module . params [ ' username ' ]
password = module . params [ ' password ' ]
server_hostname = module . params [ ' server_hostname ' ]
server_insecure = module . params [ ' server_insecure ' ]
rhsm_baseurl = module . params [ ' rhsm_baseurl ' ]
autosubscribe = module . params [ ' autosubscribe ' ] == True
activationkey = module . params [ ' activationkey ' ]
2015-03-01 14:16:44 +01:00
org_id = module . params [ ' org_id ' ]
2013-06-06 20:54:35 +02:00
pool = module . params [ ' pool ' ]
2016-04-20 17:33:08 +02:00
consumer_type = module . params [ " consumer_type " ]
consumer_name = module . params [ " consumer_name " ]
consumer_id = module . params [ " consumer_id " ]
2013-06-06 20:54:35 +02:00
# Ensure system is registered
if state == ' present ' :
# Check for missing parameters ...
if not ( activationkey or username or password ) :
module . fail_json ( msg = " Missing arguments, must supply an activationkey ( %s ) or username ( %s ) and password ( %s ) " % ( activationkey , username , password ) )
if not activationkey and not ( username and password ) :
module . fail_json ( msg = " Missing arguments, If registering without an activationkey, must supply username or password " )
# Register system
2013-06-07 20:07:00 +02:00
if rhn . is_registered :
2015-04-25 06:12:25 +02:00
if pool != ' ^$ ' :
try :
result = rhn . update_subscriptions ( pool )
2016-05-20 16:48:10 +02:00
except Exception :
e = get_exception ( )
2015-04-25 06:12:25 +02:00
module . fail_json ( msg = " Failed to update subscriptions for ' %s ' : %s " % ( server_hostname , e ) )
else :
module . exit_json ( * * result )
else :
module . exit_json ( changed = False , msg = " System already registered. " )
2013-06-06 20:54:35 +02:00
else :
try :
2013-06-07 20:07:00 +02:00
rhn . enable ( )
rhn . configure ( * * module . params )
2016-04-20 17:33:08 +02:00
rhn . register ( username , password , autosubscribe , activationkey , org_id ,
consumer_type , consumer_name , consumer_id )
2015-04-25 06:12:25 +02:00
subscribed_pool_ids = rhn . subscribe ( pool )
2016-05-20 16:48:10 +02:00
except Exception :
e = get_exception ( )
2013-06-06 20:54:35 +02:00
module . fail_json ( msg = " Failed to register with ' %s ' : %s " % ( server_hostname , e ) )
else :
2015-04-25 06:12:25 +02:00
module . exit_json ( changed = True ,
msg = " System successfully registered to ' %s ' . " % server_hostname ,
subscribed_pool_ids = subscribed_pool_ids )
2013-06-06 20:54:35 +02:00
# Ensure system is *not* registered
if state == ' absent ' :
2013-06-07 20:07:00 +02:00
if not rhn . is_registered :
2013-06-06 20:54:35 +02:00
module . exit_json ( changed = False , msg = " System already unregistered. " )
else :
try :
2013-06-07 20:07:00 +02:00
rhn . unsubscribe ( )
rhn . unregister ( )
2016-05-20 16:48:10 +02:00
except Exception :
e = get_exception ( )
2013-06-06 20:54:35 +02:00
module . fail_json ( msg = " Failed to unregister: %s " % e )
else :
module . exit_json ( changed = True , msg = " System successfully unregistered from %s . " % server_hostname )
2013-12-02 21:11:23 +01:00
# import module snippets
from ansible . module_utils . basic import *
2013-06-06 20:54:35 +02:00
main ( )