#!/usr/bin/python DOCUMENTATION = ''' --- module: subscription-manager short_description: Define interface to subscription-manager description: - Adds or removes Red Hat software channels on a system version_added: 1.0 author: James Laska notes: - this module fetches the systemid from rhn. A function to use the local systemid is provided (get_localsystem) but not integrated requirements: - none options: username: description - RHN username required: false default: null password: description: - RHN password required: false default: null server_hostname: description: - Specify an alternative RHN server required: false server_insecure: description: - Allow RHN traffic over insecure http required: false default: false rhsm_baseurl: description: - Specify CDN baseurl required: false default: false autosubscribe: description: - Upon successful registration, auto-consume available subscriptions required: false default: false activationkey: description: - supply an activation key for use with registration required: false default: null pool: description: - A pool to subscribe to (accepts regular expression syntax) required: false default: '^$' examples: - code: subscription_manager action=register username=rhsm_user password=somepass autosubscribe=true ''' import os import re import types import subprocess import ConfigParser import shlex class CommandException(Exception): pass def run_command(args): ''' Convenience method to run a command, specified as a list of arguments. Returns: * tuple - (stdout, stder, retcode) ''' # Coerce into a string if isinstance(args, str): args = shlex.split(args) # Run desired command proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) (stdout, stderr) = proc.communicate() returncode = proc.poll() if returncode != 0: cmd = ' '.join(args) raise CommandException("Command failed (%s): %s\n%s" % (returncode, cmd, stdout)) return (stdout, stderr, returncode) class RhsmPool(object): def __init__(self, **kwargs): for k,v in kwargs.items(): setattr(self, k, v) def __str__(self): return str(self.__getattribute__('_name')) def subscribe(self): (stdout, stderr, retcode) = run_command("subscription-manager subscribe --pool %s" % self.PoolId) return True class RhsmPools(object): """ This class is used for manipulating pools subscriptions with RHSM """ def __init__(self): self.products = self._load_product_list() def __iter__(self): return self.products.__iter__() def _load_product_list(self): """ Loads list of all availaible pools for system in data structure """ (stdout, stderr, retval) = run_command("subscription-manager list --available") 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 products.append(RhsmPool(_name=value, key=value)) 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 read_rhsm_config(rhsm_conf='/etc/rhsm/rhsm.conf'): ''' Load RHSM configuration from /etc/rhsm/rhsm.conf. Returns: * ConfigParser object ''' # Read RHSM defaults ... cp = ConfigParser.ConfigParser() if os.path.isfile(rhsm_conf): cp.read(rhsm_conf) # 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 is_registered(): ''' 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'] try: (stdout, stderr, retcode) = run_command(args) except CommandException, e: return False else: # Display some debug output return True def configure(**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)) run_command(args) def register(username, password, autosubscribe, activationkey): ''' 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: args.append('--activationkey "%s"' % activationkey) else: if autosubscribe: args.append('--autosubscribe') if username: args.extend(['--username', username]) if password: args.extend(['--password', password]) # Do the needful... run_command(args) def subscribe(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 available_pools = RhsmPools() for pool in available_pools.filter(regexp): pool.subscribe() def unregister(): ''' Unregister a currently registered system Raises: * Exception - if error occurs while running command ''' args = ['subscription-manager', 'unregister'] return run_command(args) def main(): # Load RHSM configuration from file cp = read_rhsm_config() module = AnsibleModule( argument_spec = dict( state = dict(default='present', choices=['present', 'absent']), username = dict(default=None, required=False), password = dict(default=None, required=False), server_hostname = dict(default=cp.get_option('server.hostname'), required=False), server_insecure = dict(default=cp.get_option('server.insecure'), required=False), rhsm_baseurl = dict(default=cp.get_option('rhsm.baseurl'), required=False), autosubscribe = dict(default=False, type='bool'), activationkey = dict(default=None, required=False), pool = dict(default='^$', required=False, type='str'), ) ) 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'] pool = module.params['pool'] # 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 if is_registered(): module.exit_json(changed=False, msg="System already registered.") else: try: configure(**module.params) register(username, password, autosubscribe, activationkey) subscribe(pool) except CommandException, e: module.fail_json(msg="Failed to register with '%s': %s" % (server_hostname, e)) else: module.exit_json(changed=True, msg="System successfully registered to '%s'." % server_hostname) # Ensure system is *not* registered if state == 'absent': if not is_registered(): module.exit_json(changed=False, msg="System already unregistered.") else: try: unregister() except CommandException, e: module.fail_json(msg="Failed to unregister: %s" % e) else: module.exit_json(changed=True, msg="System successfully unregistered from %s." % server_hostname) # include magic from lib/ansible/module_common.py #<> main()