def main(): module = AssibleModule( argument_spec=dict( name=dict(required=True), selection=dict(choices=['install', 'hold', 'deinstall', 'purge'], required=True)), supports_check_mode=True, ) dpkg = module.get_bin_path('dpkg', True) name = module.params['name'] selection = module.params['selection'] # Get current settings. rc, out, err = module.run_command([dpkg, '--get-selections', name], check_rc=True) if not out: current = 'not present' else: current = out.split()[1] changed = current != selection if module.check_mode or not changed: module.exit_json(changed=changed, before=current, after=selection) module.run_command([dpkg, '--set-selections'], data="%s %s" % (name, selection), check_rc=True) module.exit_json(changed=changed, before=current, after=selection)
def main(): module = AssibleModule( argument_spec=dict( database=dict(type='str', required=True), key=dict(type='str'), service=dict(type='str'), split=dict(type='str'), fail_key=dict(type='bool', default=True), ), supports_check_mode=True, ) colon = ['passwd', 'shadow', 'group', 'gshadow'] database = module.params['database'] key = module.params.get('key') split = module.params.get('split') service = module.params.get('service') fail_key = module.params.get('fail_key') getent_bin = module.get_bin_path('getent', True) if key is not None: cmd = [getent_bin, database, key] else: cmd = [getent_bin, database] if service is not None: cmd.extend(['-s', service]) if split is None and database in colon: split = ':' try: rc, out, err = module.run_command(cmd) except Exception as e: module.fail_json(msg=to_native(e), exception=traceback.format_exc()) msg = "Unexpected failure!" dbtree = 'getent_%s' % database results = {dbtree: {}} if rc == 0: for line in out.splitlines(): record = line.split(split) results[dbtree][record[0]] = record[1:] module.exit_json(assible_facts=results) elif rc == 1: msg = "Missing arguments, or database unknown." elif rc == 2: msg = "One or more supplied key could not be found in the database." if not fail_key: results[dbtree][key] = None module.exit_json(assible_facts=results, msg=msg) elif rc == 3: msg = "Enumeration not supported on this database." module.fail_json(msg=msg)
def main(): module = AssibleModule( # not checking because of daisy chain to file module argument_spec=dict( src=dict(type='path', required=True), delimiter=dict(type='str'), dest=dict(type='path', required=True), backup=dict(type='bool', default=False), remote_src=dict(type='bool', default=True), regexp=dict(type='str'), ignore_hidden=dict(type='bool', default=False), validate=dict(type='str'), ), add_file_common_args=True, ) changed = False path_hash = None dest_hash = None src = module.params['src'] dest = module.params['dest'] backup = module.params['backup'] delimiter = module.params['delimiter'] regexp = module.params['regexp'] compiled_regexp = None ignore_hidden = module.params['ignore_hidden'] validate = module.params.get('validate', None) result = dict(src=src, dest=dest) if not os.path.exists(src): module.fail_json(msg="Source (%s) does not exist" % src) if not os.path.isdir(src): module.fail_json(msg="Source (%s) is not a directory" % src) if regexp is not None: try: compiled_regexp = re.compile(regexp) except re.error as e: module.fail_json(msg="Invalid Regexp (%s) in \"%s\"" % (to_native(e), regexp)) if validate and "%s" not in validate: module.fail_json(msg="validate must contain %%s: %s" % validate) path = assemble_from_fragments(src, delimiter, compiled_regexp, ignore_hidden, module.tmpdir) path_hash = module.sha1(path) result['checksum'] = path_hash # Backwards compat. This won't return data if FIPS mode is active try: pathmd5 = module.md5(path) except ValueError: pathmd5 = None result['md5sum'] = pathmd5 if os.path.exists(dest): dest_hash = module.sha1(dest) if path_hash != dest_hash: if validate: (rc, out, err) = module.run_command(validate % path) result['validation'] = dict(rc=rc, stdout=out, stderr=err) if rc != 0: cleanup(path) module.fail_json(msg="failed to validate: rc:%s error:%s" % (rc, err)) if backup and dest_hash is not None: result['backup_file'] = module.backup_local(dest) module.atomic_move(path, dest, unsafe_writes=module.params['unsafe_writes']) changed = True cleanup(path, result) # handle file permissions file_args = module.load_file_common_arguments(module.params) result['changed'] = module.set_fs_attributes_if_different( file_args, changed) # Mission complete result['msg'] = "OK" module.exit_json(**result)
def main(): module = AssibleModule( argument_spec=dict( vg=dict(type='str', required=True), pvs=dict(type='list'), pesize=dict(type='str', default='4'), pv_options=dict(type='str', default=''), vg_options=dict(type='str', default=''), state=dict(type='str', default='present', choices=['absent', 'present']), force=dict(type='bool', default=False), ), supports_check_mode=True, ) vg = module.params['vg'] state = module.params['state'] force = module.boolean(module.params['force']) pesize = module.params['pesize'] pvoptions = module.params['pv_options'].split() vgoptions = module.params['vg_options'].split() dev_list = [] if module.params['pvs']: dev_list = list(module.params['pvs']) elif state == 'present': module.fail_json(msg="No physical volumes given.") # LVM always uses real paths not symlinks so replace symlinks with actual path for idx, dev in enumerate(dev_list): dev_list[idx] = os.path.realpath(dev) if state == 'present': # check given devices for test_dev in dev_list: if not os.path.exists(test_dev): module.fail_json(msg="Device %s not found." % test_dev) # get pv list pvs_cmd = module.get_bin_path('pvs', True) if dev_list: pvs_filter_pv_name = ' || '.join( 'pv_name = {0}'.format(x) for x in itertools.chain(dev_list, module.params['pvs'])) pvs_filter_vg_name = 'vg_name = {0}'.format(vg) pvs_filter = "--select '{0} || {1}' ".format( pvs_filter_pv_name, pvs_filter_vg_name) else: pvs_filter = '' rc, current_pvs, err = module.run_command( "%s --noheadings -o pv_name,vg_name --separator ';' %s" % (pvs_cmd, pvs_filter)) if rc != 0: module.fail_json(msg="Failed executing pvs command.", rc=rc, err=err) # check pv for devices pvs = parse_pvs(module, current_pvs) used_pvs = [ pv for pv in pvs if pv['name'] in dev_list and pv['vg_name'] and pv['vg_name'] != vg ] if used_pvs: module.fail_json(msg="Device %s is already in %s volume group." % (used_pvs[0]['name'], used_pvs[0]['vg_name'])) vgs_cmd = module.get_bin_path('vgs', True) rc, current_vgs, err = module.run_command( "%s --noheadings -o vg_name,pv_count,lv_count --separator ';'" % vgs_cmd) if rc != 0: module.fail_json(msg="Failed executing vgs command.", rc=rc, err=err) changed = False vgs = parse_vgs(current_vgs) for test_vg in vgs: if test_vg['name'] == vg: this_vg = test_vg break else: this_vg = None if this_vg is None: if state == 'present': # create VG if module.check_mode: changed = True else: # create PV pvcreate_cmd = module.get_bin_path('pvcreate', True) for current_dev in dev_list: rc, _, err = module.run_command( [pvcreate_cmd] + pvoptions + ['-f', str(current_dev)]) if rc == 0: changed = True else: module.fail_json( msg="Creating physical volume '%s' failed" % current_dev, rc=rc, err=err) vgcreate_cmd = module.get_bin_path('vgcreate') rc, _, err = module.run_command([vgcreate_cmd] + vgoptions + ['-s', pesize, vg] + dev_list) if rc == 0: changed = True else: module.fail_json(msg="Creating volume group '%s' failed" % vg, rc=rc, err=err) else: if state == 'absent': if module.check_mode: module.exit_json(changed=True) else: if this_vg['lv_count'] == 0 or force: # remove VG vgremove_cmd = module.get_bin_path('vgremove', True) rc, _, err = module.run_command("%s --force %s" % (vgremove_cmd, vg)) if rc == 0: module.exit_json(changed=True) else: module.fail_json( msg="Failed to remove volume group %s" % (vg), rc=rc, err=err) else: module.fail_json( msg= "Refuse to remove non-empty volume group %s without force=yes" % (vg)) # resize VG current_devs = [ os.path.realpath(pv['name']) for pv in pvs if pv['vg_name'] == vg ] devs_to_remove = list(set(current_devs) - set(dev_list)) devs_to_add = list(set(dev_list) - set(current_devs)) if devs_to_add or devs_to_remove: if module.check_mode: changed = True else: if devs_to_add: devs_to_add_string = ' '.join(devs_to_add) # create PV pvcreate_cmd = module.get_bin_path('pvcreate', True) for current_dev in devs_to_add: rc, _, err = module.run_command( [pvcreate_cmd] + pvoptions + ['-f', str(current_dev)]) if rc == 0: changed = True else: module.fail_json( msg="Creating physical volume '%s' failed" % current_dev, rc=rc, err=err) # add PV to our VG vgextend_cmd = module.get_bin_path('vgextend', True) rc, _, err = module.run_command( "%s %s %s" % (vgextend_cmd, vg, devs_to_add_string)) if rc == 0: changed = True else: module.fail_json(msg="Unable to extend %s by %s." % (vg, devs_to_add_string), rc=rc, err=err) # remove some PV from our VG if devs_to_remove: devs_to_remove_string = ' '.join(devs_to_remove) vgreduce_cmd = module.get_bin_path('vgreduce', True) rc, _, err = module.run_command( "%s --force %s %s" % (vgreduce_cmd, vg, devs_to_remove_string)) if rc == 0: changed = True else: module.fail_json(msg="Unable to reduce %s by %s." % (vg, devs_to_remove_string), rc=rc, err=err) module.exit_json(changed=changed)
def main(): state_map = dict( present=['install'], absent=['uninstall', '-y'], latest=['install', '-U'], forcereinstall=['install', '-U', '--force-reinstall'], ) module = AssibleModule( argument_spec=dict( state=dict(type='str', default='present', choices=state_map.keys()), name=dict(type='list', elements='str'), version=dict(type='str'), requirements=dict(type='str'), virtualenv=dict(type='path'), virtualenv_site_packages=dict(type='bool', default=False), virtualenv_command=dict(type='path', default='virtualenv'), virtualenv_python=dict(type='str'), extra_args=dict(type='str'), editable=dict(type='bool', default=False), chdir=dict(type='path'), executable=dict(type='path'), umask=dict(type='str'), ), required_one_of=[['name', 'requirements']], mutually_exclusive=[['name', 'requirements'], ['executable', 'virtualenv']], supports_check_mode=True, ) if not HAS_SETUPTOOLS: module.fail_json(msg=missing_required_lib("setuptools"), exception=SETUPTOOLS_IMP_ERR) state = module.params['state'] name = module.params['name'] version = module.params['version'] requirements = module.params['requirements'] extra_args = module.params['extra_args'] chdir = module.params['chdir'] umask = module.params['umask'] env = module.params['virtualenv'] venv_created = False if env and chdir: env = os.path.join(chdir, env) if umask and not isinstance(umask, int): try: umask = int(umask, 8) except Exception: module.fail_json(msg="umask must be an octal integer", details=to_native(sys.exc_info()[1])) old_umask = None if umask is not None: old_umask = os.umask(umask) try: if state == 'latest' and version is not None: module.fail_json(msg='version is incompatible with state=latest') if chdir is None: # this is done to avoid permissions issues with privilege escalation and virtualenvs chdir = tempfile.gettempdir() err = '' out = '' if env: if not os.path.exists(os.path.join(env, 'bin', 'activate')): venv_created = True out, err = setup_virtualenv(module, env, chdir, out, err) pip = _get_pip(module, env, module.params['executable']) cmd = [pip] + state_map[state] # If there's a virtualenv we want things we install to be able to use other # installations that exist as binaries within this virtualenv. Example: we # install cython and then gevent -- gevent needs to use the cython binary, # not just a python package that will be found by calling the right python. # So if there's a virtualenv, we add that bin/ to the beginning of the PATH # in run_command by setting path_prefix here. path_prefix = None if env: path_prefix = "/".join(pip.split('/')[:-1]) # Automatically apply -e option to extra_args when source is a VCS url. VCS # includes those beginning with svn+, git+, hg+ or bzr+ has_vcs = False if name: for pkg in name: if pkg and _is_vcs_url(pkg): has_vcs = True break # convert raw input package names to Package instances packages = [Package(pkg) for pkg in _recover_package_name(name)] # check invalid combination of arguments if version is not None: if len(packages) > 1: module.fail_json( msg="'version' argument is ambiguous when installing multiple package distributions. " "Please specify version restrictions next to each package in 'name' argument." ) if packages[0].has_version_specifier: module.fail_json( msg="The 'version' argument conflicts with any version specifier provided along with a package name. " "Please keep the version specifier, but remove the 'version' argument." ) # if the version specifier is provided by version, append that into the package packages[0] = Package(to_native(packages[0]), version) if module.params['editable']: args_list = [] # used if extra_args is not used at all if extra_args: args_list = extra_args.split(' ') if '-e' not in args_list: args_list.append('-e') # Ok, we will reconstruct the option string extra_args = ' '.join(args_list) if extra_args: cmd.extend(shlex.split(extra_args)) if name: cmd.extend(to_native(p) for p in packages) elif requirements: cmd.extend(['-r', requirements]) else: module.exit_json( changed=False, warnings=["No valid name or requirements file found."], ) if module.check_mode: if extra_args or requirements or state == 'latest' or not name: module.exit_json(changed=True) pkg_cmd, out_pip, err_pip = _get_packages(module, pip, chdir) out += out_pip err += err_pip changed = False if name: pkg_list = [p for p in out.split('\n') if not p.startswith('You are using') and not p.startswith('You should consider') and p] if pkg_cmd.endswith(' freeze') and ('pip' in name or 'setuptools' in name): # Older versions of pip (pre-1.3) do not have pip list. # pip freeze does not list setuptools or pip in its output # So we need to get those via a specialcase for pkg in ('setuptools', 'pip'): if pkg in name: formatted_dep = _get_package_info(module, pkg, env) if formatted_dep is not None: pkg_list.append(formatted_dep) out += '%s\n' % formatted_dep for package in packages: is_present = _is_present(module, package, pkg_list, pkg_cmd) if (state == 'present' and not is_present) or (state == 'absent' and is_present): changed = True break module.exit_json(changed=changed, cmd=pkg_cmd, stdout=out, stderr=err) out_freeze_before = None if requirements or has_vcs: _, out_freeze_before, _ = _get_packages(module, pip, chdir) rc, out_pip, err_pip = module.run_command(cmd, path_prefix=path_prefix, cwd=chdir) out += out_pip err += err_pip if rc == 1 and state == 'absent' and \ ('not installed' in out_pip or 'not installed' in err_pip): pass # rc is 1 when attempting to uninstall non-installed package elif rc != 0: _fail(module, cmd, out, err) if state == 'absent': changed = 'Successfully uninstalled' in out_pip else: if out_freeze_before is None: changed = 'Successfully installed' in out_pip else: _, out_freeze_after, _ = _get_packages(module, pip, chdir) changed = out_freeze_before != out_freeze_after changed = changed or venv_created module.exit_json(changed=changed, cmd=cmd, name=name, version=version, state=state, requirements=requirements, virtualenv=env, stdout=out, stderr=err) finally: if old_umask is not None: os.umask(old_umask)
def main(): module = AssibleModule( argument_spec=dict( name=dict(required=True, type='str', aliases=['service']), state=dict(choices=['started', 'stopped', 'restarted', 'reloaded'], type='str'), enabled=dict(type='bool'), sleep=dict(type='int', default=1), pattern=dict(type='str'), arguments=dict(type='str', aliases=['args']), runlevels=dict(type='list', elements='str'), daemonize=dict(type='bool', default=False), ), supports_check_mode=True, required_one_of=[['state', 'enabled']], ) name = module.params['name'] action = module.params['state'] enabled = module.params['enabled'] runlevels = module.params['runlevels'] pattern = module.params['pattern'] sleep_for = module.params['sleep'] rc = 0 out = err = '' result = { 'name': name, 'changed': False, 'status': {} } # ensure service exists, get script name fail_if_missing(module, sysv_exists(name), name) script = get_sysv_script(name) # locate binaries for service management paths = ['/sbin', '/usr/sbin', '/bin', '/usr/bin'] binaries = ['chkconfig', 'update-rc.d', 'insserv', 'service'] # Keeps track of the service status for various runlevels because we can # operate on multiple runlevels at once runlevel_status = {} location = {} for binary in binaries: location[binary] = module.get_bin_path(binary, opt_dirs=paths) # figure out enable status if runlevels: for rl in runlevels: runlevel_status.setdefault(rl, {}) runlevel_status[rl]["enabled"] = sysv_is_enabled(name, runlevel=rl) else: runlevel_status["enabled"] = sysv_is_enabled(name) # figure out started status, everyone does it different! is_started = False worked = False # user knows other methods fail and supplied pattern if pattern: worked = is_started = get_ps(module, pattern) else: if location.get('service'): # standard tool that has been 'destandarized' by reimplementation in other OS/distros cmd = '%s %s status' % (location['service'], name) elif script: # maybe script implements status (not LSB) cmd = '%s status' % script else: module.fail_json(msg="Unable to determine service status") (rc, out, err) = module.run_command(cmd) if not rc == -1: # special case if name == 'iptables' and "ACCEPT" in out: worked = True is_started = True # check output messages, messy but sadly more reliable than rc if not worked and out.count('\n') <= 1: cleanout = out.lower().replace(name.lower(), '') for stopped in ['stop', 'is dead ', 'dead but ', 'could not access pid file', 'inactive']: if stopped in cleanout: worked = True break if not worked: for started_status in ['run', 'start', 'active']: if started_status in cleanout and "not " not in cleanout: is_started = True worked = True break # hope rc is not lying to us, use often used 'bad' returns if not worked and rc in [1, 2, 3, 4, 69]: worked = True if not worked: # hail mary if rc == 0: is_started = True worked = True # ps for luck, can only assure positive match elif get_ps(module, name): is_started = True worked = True module.warn("Used ps output to match service name and determine it is up, this is very unreliable") if not worked: module.warn("Unable to determine if service is up, assuming it is down") ########################################################################### # BEGIN: Enable/Disable result['status'].setdefault('enabled', {}) result['status']['enabled']['changed'] = False result['status']['enabled']['rc'] = None result['status']['enabled']['stdout'] = None result['status']['enabled']['stderr'] = None if runlevels: result['status']['enabled']['runlevels'] = runlevels for rl in runlevels: if enabled != runlevel_status[rl]["enabled"]: result['changed'] = True result['status']['enabled']['changed'] = True if not module.check_mode and result['changed']: # Perform enable/disable here if enabled: if location.get('update-rc.d'): (rc, out, err) = module.run_command("%s %s enable %s" % (location['update-rc.d'], name, ' '.join(runlevels))) elif location.get('chkconfig'): (rc, out, err) = module.run_command("%s --level %s %s on" % (location['chkconfig'], ''.join(runlevels), name)) else: if location.get('update-rc.d'): (rc, out, err) = module.run_command("%s %s disable %s" % (location['update-rc.d'], name, ' '.join(runlevels))) elif location.get('chkconfig'): (rc, out, err) = module.run_command("%s --level %s %s off" % (location['chkconfig'], ''.join(runlevels), name)) else: if enabled is not None and enabled != runlevel_status["enabled"]: result['changed'] = True result['status']['enabled']['changed'] = True if not module.check_mode and result['changed']: # Perform enable/disable here if enabled: if location.get('update-rc.d'): (rc, out, err) = module.run_command("%s %s defaults" % (location['update-rc.d'], name)) elif location.get('chkconfig'): (rc, out, err) = module.run_command("%s %s on" % (location['chkconfig'], name)) else: if location.get('update-rc.d'): (rc, out, err) = module.run_command("%s %s disable" % (location['update-rc.d'], name)) elif location.get('chkconfig'): (rc, out, err) = module.run_command("%s %s off" % (location['chkconfig'], name)) # Assigned above, might be useful is something goes sideways if not module.check_mode and result['status']['enabled']['changed']: result['status']['enabled']['rc'] = rc result['status']['enabled']['stdout'] = out result['status']['enabled']['stderr'] = err rc, out, err = None, None, None if "illegal runlevel specified" in result['status']['enabled']['stderr']: module.fail_json(msg="Illegal runlevel specified for enable operation on service %s" % name, **result) # END: Enable/Disable ########################################################################### ########################################################################### # BEGIN: state result['status'].setdefault(module.params['state'], {}) result['status'][module.params['state']]['changed'] = False result['status'][module.params['state']]['rc'] = None result['status'][module.params['state']]['stdout'] = None result['status'][module.params['state']]['stderr'] = None if action: action = re.sub(r'p?ed$', '', action.lower()) def runme(doit): args = module.params['arguments'] cmd = "%s %s %s" % (script, doit, "" if args is None else args) # how to run if module.params['daemonize']: (rc, out, err) = daemonize(module, cmd) else: (rc, out, err) = module.run_command(cmd) # FIXME: ERRORS if rc != 0: module.fail_json(msg="Failed to %s service: %s" % (action, name), rc=rc, stdout=out, stderr=err) return (rc, out, err) if action == 'restart': result['changed'] = True result['status'][module.params['state']]['changed'] = True if not module.check_mode: # cannot rely on existing 'restart' in init script for dothis in ['stop', 'start']: (rc, out, err) = runme(dothis) if sleep_for: sleep(sleep_for) elif is_started != (action == 'start'): result['changed'] = True result['status'][module.params['state']]['changed'] = True if not module.check_mode: rc, out, err = runme(action) elif is_started == (action == 'stop'): result['changed'] = True result['status'][module.params['state']]['changed'] = True if not module.check_mode: rc, out, err = runme(action) if not module.check_mode and result['status'][module.params['state']]['changed']: result['status'][module.params['state']]['rc'] = rc result['status'][module.params['state']]['stdout'] = out result['status'][module.params['state']]['stderr'] = err rc, out, err = None, None, None # END: state ########################################################################### module.exit_json(**result)
def main(): # initialize module = AssibleModule( argument_spec=dict( name=dict(type='str', aliases=['service', 'unit']), state=dict(type='str', choices=['reloaded', 'restarted', 'started', 'stopped']), enabled=dict(type='bool'), force=dict(type='bool'), masked=dict(type='bool'), daemon_reload=dict(type='bool', default=False, aliases=['daemon-reload']), daemon_reexec=dict(type='bool', default=False, aliases=['daemon-reexec']), scope=dict(type='str', default='system', choices=['system', 'user', 'global']), no_block=dict(type='bool', default=False), ), supports_check_mode=True, required_one_of=[[ 'state', 'enabled', 'masked', 'daemon_reload', 'daemon_reexec' ]], required_by=dict( state=('name', ), enabled=('name', ), masked=('name', ), ), ) unit = module.params['name'] if unit is not None: for globpattern in (r"*", r"?", r"["): if globpattern in unit: module.fail_json( msg= "This module does not currently support using glob patterns, found '%s' in service name: %s" % (globpattern, unit)) systemctl = module.get_bin_path('systemctl', True) if os.getenv('XDG_RUNTIME_DIR') is None: os.environ['XDG_RUNTIME_DIR'] = '/run/user/%s' % os.geteuid() ''' Set CLI options depending on params ''' # if scope is 'system' or None, we can ignore as there is no extra switch. # The other choices match the corresponding switch if module.params['scope'] != 'system': systemctl += " --%s" % module.params['scope'] if module.params['no_block']: systemctl += " --no-block" if module.params['force']: systemctl += " --force" rc = 0 out = err = '' result = dict( name=unit, changed=False, status=dict(), ) # Run daemon-reload first, if requested if module.params['daemon_reload'] and not module.check_mode: (rc, out, err) = module.run_command("%s daemon-reload" % (systemctl)) if rc != 0: module.fail_json(msg='failure %d during daemon-reload: %s' % (rc, err)) # Run daemon-reexec if module.params['daemon_reexec'] and not module.check_mode: (rc, out, err) = module.run_command("%s daemon-reexec" % (systemctl)) if rc != 0: module.fail_json(msg='failure %d during daemon-reexec: %s' % (rc, err)) if unit: found = False is_initd = sysv_exists(unit) is_systemd = False # check service data, cannot error out on rc as it changes across versions, assume not found (rc, out, err) = module.run_command("%s show '%s'" % (systemctl, unit)) if rc == 0 and not (request_was_ignored(out) or request_was_ignored(err)): # load return of systemctl show into dictionary for easy access and return if out: result['status'] = parse_systemctl_show( to_native(out).split('\n')) is_systemd = 'LoadState' in result[ 'status'] and result['status']['LoadState'] != 'not-found' is_masked = 'LoadState' in result['status'] and result[ 'status']['LoadState'] == 'masked' # Check for loading error if is_systemd and not is_masked and 'LoadError' in result[ 'status']: module.fail_json(msg="Error loading unit file '%s': %s" % (unit, result['status']['LoadError'])) else: # list taken from man systemctl(1) for systemd 244 valid_enabled_states = [ "enabled", "enabled-runtime", "linked", "linked-runtime", "masked", "masked-runtime", "static", "indirect", "disabled", "generated", "transient" ] (rc, out, err) = module.run_command("%s is-enabled '%s'" % (systemctl, unit)) if out.strip() in valid_enabled_states: is_systemd = True else: # fallback list-unit-files as show does not work on some systems (chroot) # not used as primary as it skips some services (like those using init.d) and requires .service/etc notation (rc, out, err) = module.run_command("%s list-unit-files '%s'" % (systemctl, unit)) if rc == 0: is_systemd = True else: # Check for systemctl command module.run_command(systemctl, check_rc=True) # Does service exist? found = is_systemd or is_initd if is_initd and not is_systemd: module.warn( 'The service (%s) is actually an init script but the system is managed by systemd' % unit) # mask/unmask the service, if requested, can operate on services before they are installed if module.params['masked'] is not None: # state is not masked unless systemd affirms otherwise (rc, out, err) = module.run_command("%s is-enabled '%s'" % (systemctl, unit)) masked = out.strip() == "masked" if masked != module.params['masked']: result['changed'] = True if module.params['masked']: action = 'mask' else: action = 'unmask' if not module.check_mode: (rc, out, err) = module.run_command( "%s %s '%s'" % (systemctl, action, unit)) if rc != 0: # some versions of system CAN mask/unmask non existing services, we only fail on missing if they don't fail_if_missing(module, found, unit, msg='host') # Enable/disable service startup at boot if requested if module.params['enabled'] is not None: if module.params['enabled']: action = 'enable' else: action = 'disable' fail_if_missing(module, found, unit, msg='host') # do we need to enable the service? enabled = False (rc, out, err) = module.run_command("%s is-enabled '%s'" % (systemctl, unit)) # check systemctl result or if it is a init script if rc == 0: enabled = True elif rc == 1: # if not a user or global user service and both init script and unit file exist stdout should have enabled/disabled, otherwise use rc entries if module.params['scope'] == 'system' and \ is_initd and \ not out.strip().endswith('disabled') and \ sysv_is_enabled(unit): enabled = True # default to current state result['enabled'] = enabled # Change enable/disable if needed if enabled != module.params['enabled']: result['changed'] = True if not module.check_mode: (rc, out, err) = module.run_command( "%s %s '%s'" % (systemctl, action, unit)) if rc != 0: module.fail_json(msg="Unable to %s service %s: %s" % (action, unit, out + err)) result['enabled'] = not enabled # set service state if requested if module.params['state'] is not None: fail_if_missing(module, found, unit, msg="host") # default to desired state result['state'] = module.params['state'] # What is current service state? if 'ActiveState' in result['status']: action = None if module.params['state'] == 'started': if not is_running_service(result['status']): action = 'start' elif module.params['state'] == 'stopped': if is_running_service( result['status']) or is_deactivating_service( result['status']): action = 'stop' else: if not is_running_service(result['status']): action = 'start' else: action = module.params[ 'state'][: -2] # remove 'ed' from restarted/reloaded result['state'] = 'started' if action: result['changed'] = True if not module.check_mode: (rc, out, err) = module.run_command( "%s %s '%s'" % (systemctl, action, unit)) if rc != 0: module.fail_json( msg="Unable to %s service %s: %s" % (action, unit, err)) # check for chroot elif is_chroot(module) or os.environ.get('SYSTEMD_OFFLINE') == '1': module.warn( "Target is a chroot or systemd is offline. This can lead to false positives or prevent the init system tools from working." ) else: # this should not happen? module.fail_json(msg="Service is in unknown state", status=result['status']) module.exit_json(**result)
def main(): # the command module is the one assible module that does not take key=value args # hence don't copy this one if you are looking to build others! module = AssibleModule( argument_spec=dict( _raw_params=dict(), _uses_shell=dict(type='bool', default=False), argv=dict(type='list', elements='str'), chdir=dict(type='path'), executable=dict(), creates=dict(type='path'), removes=dict(type='path'), # The default for this really comes from the action plugin warn=dict(type='bool', default=False, removed_in_version='2.14', removed_from_collection='assible.builtin'), stdin=dict(required=False), stdin_add_newline=dict(type='bool', default=True), strip_empty_ends=dict(type='bool', default=True), ), supports_check_mode=True, ) shell = module.params['_uses_shell'] chdir = module.params['chdir'] executable = module.params['executable'] args = module.params['_raw_params'] argv = module.params['argv'] creates = module.params['creates'] removes = module.params['removes'] warn = module.params['warn'] stdin = module.params['stdin'] stdin_add_newline = module.params['stdin_add_newline'] strip = module.params['strip_empty_ends'] if not shell and executable: module.warn("As of Assible 2.4, the parameter 'executable' is no longer supported with the 'command' module. Not using '%s'." % executable) executable = None if (not args or args.strip() == '') and not argv: module.fail_json(rc=256, msg="no command given") if args and argv: module.fail_json(rc=256, msg="only command or argv can be given, not both") if not shell and args: args = shlex.split(args) args = args or argv # All args must be strings if is_iterable(args, include_strings=False): args = [to_native(arg, errors='surrogate_or_strict', nonstring='simplerepr') for arg in args] if chdir: try: chdir = to_bytes(os.path.abspath(chdir), errors='surrogate_or_strict') except ValueError as e: module.fail_json(msg='Unable to use supplied chdir: %s' % to_text(e)) try: os.chdir(chdir) except (IOError, OSError) as e: module.fail_json(msg='Unable to change directory before execution: %s' % to_text(e)) if creates: # do not run the command if the line contains creates=filename # and the filename already exists. This allows idempotence # of command executions. if glob.glob(creates): module.exit_json( cmd=args, stdout="skipped, since %s exists" % creates, changed=False, rc=0 ) if removes: # do not run the command if the line contains removes=filename # and the filename does not exist. This allows idempotence # of command executions. if not glob.glob(removes): module.exit_json( cmd=args, stdout="skipped, since %s does not exist" % removes, changed=False, rc=0 ) if warn: check_command(module, args) startd = datetime.datetime.now() if not module.check_mode: rc, out, err = module.run_command(args, executable=executable, use_unsafe_shell=shell, encoding=None, data=stdin, binary_data=(not stdin_add_newline)) elif creates or removes: rc = 0 out = err = b'Command would have run if not in check mode' else: module.exit_json(msg="skipped, running in check mode", skipped=True) endd = datetime.datetime.now() delta = endd - startd if strip: out = out.rstrip(b"\r\n") err = err.rstrip(b"\r\n") result = dict( cmd=args, stdout=out, stderr=err, rc=rc, start=str(startd), end=str(endd), delta=str(delta), changed=True, ) if rc != 0: module.fail_json(msg='non-zero return code', **result) module.exit_json(**result)
def main(): global module module = AssibleModule( # not checking because of daisy chain to file module argument_spec=dict( src=dict(type='path'), _original_basename=dict( type='str' ), # used to handle 'dest is a directory' via template, a slight hack content=dict(type='str', no_log=True), dest=dict(type='path', required=True), backup=dict(type='bool', default=False), force=dict(type='bool', default=True, aliases=['thirsty']), validate=dict(type='str'), directory_mode=dict(type='raw'), remote_src=dict(type='bool'), local_follow=dict(type='bool'), checksum=dict(type='str'), follow=dict(type='bool', default=False), ), add_file_common_args=True, supports_check_mode=True, ) if module.params.get('thirsty'): module.deprecate( 'The alias "thirsty" has been deprecated and will be removed, use "force" instead', version='2.13', collection_name='assible.builtin') src = module.params['src'] b_src = to_bytes(src, errors='surrogate_or_strict') dest = module.params['dest'] # Make sure we always have a directory component for later processing if os.path.sep not in dest: dest = '.{0}{1}'.format(os.path.sep, dest) b_dest = to_bytes(dest, errors='surrogate_or_strict') backup = module.params['backup'] force = module.params['force'] _original_basename = module.params.get('_original_basename', None) validate = module.params.get('validate', None) follow = module.params['follow'] local_follow = module.params['local_follow'] mode = module.params['mode'] owner = module.params['owner'] group = module.params['group'] remote_src = module.params['remote_src'] checksum = module.params['checksum'] if not os.path.exists(b_src): module.fail_json(msg="Source %s not found" % (src)) if not os.access(b_src, os.R_OK): module.fail_json(msg="Source %s not readable" % (src)) # Preserve is usually handled in the action plugin but mode + remote_src has to be done on the # remote host if module.params['mode'] == 'preserve': module.params['mode'] = '0%03o' % stat.S_IMODE(os.stat(b_src).st_mode) mode = module.params['mode'] checksum_dest = None if os.path.isfile(src): checksum_src = module.sha1(src) else: checksum_src = None # Backwards compat only. This will be None in FIPS mode try: if os.path.isfile(src): md5sum_src = module.md5(src) else: md5sum_src = None except ValueError: md5sum_src = None changed = False if checksum and checksum_src != checksum: module.fail_json( msg= 'Copied file does not match the expected checksum. Transfer failed.', checksum=checksum_src, expected_checksum=checksum) # Special handling for recursive copy - create intermediate dirs if dest.endswith(os.sep): if _original_basename: dest = os.path.join(dest, _original_basename) b_dest = to_bytes(dest, errors='surrogate_or_strict') dirname = os.path.dirname(dest) b_dirname = to_bytes(dirname, errors='surrogate_or_strict') if not os.path.exists(b_dirname): try: (pre_existing_dir, new_directory_list) = split_pre_existing_dir(dirname) except AssibleModuleError as e: e.result['msg'] += ' Could not copy to {0}'.format(dest) module.fail_json(**e.results) os.makedirs(b_dirname) directory_args = module.load_file_common_arguments(module.params) directory_mode = module.params["directory_mode"] if directory_mode is not None: directory_args['mode'] = directory_mode else: directory_args['mode'] = None adjust_recursive_directory_permissions(pre_existing_dir, new_directory_list, module, directory_args, changed) if os.path.isdir(b_dest): basename = os.path.basename(src) if _original_basename: basename = _original_basename dest = os.path.join(dest, basename) b_dest = to_bytes(dest, errors='surrogate_or_strict') if os.path.exists(b_dest): if os.path.islink(b_dest) and follow: b_dest = os.path.realpath(b_dest) dest = to_native(b_dest, errors='surrogate_or_strict') if not force: module.exit_json(msg="file already exists", src=src, dest=dest, changed=False) if os.access(b_dest, os.R_OK) and os.path.isfile(b_dest): checksum_dest = module.sha1(dest) else: if not os.path.exists(os.path.dirname(b_dest)): try: # os.path.exists() can return false in some # circumstances where the directory does not have # the execute bit for the current user set, in # which case the stat() call will raise an OSError os.stat(os.path.dirname(b_dest)) except OSError as e: if "permission denied" in to_native(e).lower(): module.fail_json( msg="Destination directory %s is not accessible" % (os.path.dirname(dest))) module.fail_json(msg="Destination directory %s does not exist" % (os.path.dirname(dest))) if not os.access(os.path.dirname(b_dest), os.W_OK) and not module.params['unsafe_writes']: module.fail_json(msg="Destination %s not writable" % (os.path.dirname(dest))) backup_file = None if checksum_src != checksum_dest or os.path.islink(b_dest): if not module.check_mode: try: if backup: if os.path.exists(b_dest): backup_file = module.backup_local(dest) # allow for conversion from symlink. if os.path.islink(b_dest): os.unlink(b_dest) open(b_dest, 'w').close() if validate: # if we have a mode, make sure we set it on the temporary # file source as some validations may require it if mode is not None: module.set_mode_if_different(src, mode, False) if owner is not None: module.set_owner_if_different(src, owner, False) if group is not None: module.set_group_if_different(src, group, False) if "%s" not in validate: module.fail_json(msg="validate must contain %%s: %s" % (validate)) (rc, out, err) = module.run_command(validate % src) if rc != 0: module.fail_json(msg="failed to validate", exit_status=rc, stdout=out, stderr=err) b_mysrc = b_src if remote_src and os.path.isfile(b_src): _, b_mysrc = tempfile.mkstemp(dir=os.path.dirname(b_dest)) shutil.copyfile(b_src, b_mysrc) try: shutil.copystat(b_src, b_mysrc) except OSError as err: if err.errno == errno.ENOSYS and mode == "preserve": module.warn("Unable to copy stats {0}".format( to_native(b_src))) else: raise # might be needed below if PY3 and hasattr(os, 'listxattr'): try: src_has_acls = 'system.posix_acl_access' in os.listxattr( src) except Exception as e: # assume unwanted ACLs by default src_has_acls = True module.atomic_move( b_mysrc, dest, unsafe_writes=module.params['unsafe_writes']) if PY3 and hasattr(os, 'listxattr') and platform.system( ) == 'Linux' and not remote_src: # atomic_move used above to copy src into dest might, in some cases, # use shutil.copy2 which in turn uses shutil.copystat. # Since Python 3.3, shutil.copystat copies file extended attributes: # https://docs.python.org/3/library/shutil.html#shutil.copystat # os.listxattr (along with others) was added to handle the operation. # This means that on Python 3 we are copying the extended attributes which includes # the ACLs on some systems - further limited to Linux as the documentation above claims # that the extended attributes are copied only on Linux. Also, os.listxattr is only # available on Linux. # If not remote_src, then the file was copied from the controller. In that # case, any filesystem ACLs are artifacts of the copy rather than preservation # of existing attributes. Get rid of them: if src_has_acls: # FIXME If dest has any default ACLs, there are not applied to src now because # they were overridden by copystat. Should/can we do anything about this? # 'system.posix_acl_default' in os.listxattr(os.path.dirname(b_dest)) try: clear_facls(dest) except ValueError as e: if 'setfacl' in to_native(e): # No setfacl so we're okay. The controller couldn't have set a facl # without the setfacl command pass else: raise except RuntimeError as e: # setfacl failed. if 'Operation not supported' in to_native(e): # The file system does not support ACLs. pass else: raise except (IOError, OSError): module.fail_json(msg="failed to copy: %s to %s" % (src, dest), traceback=traceback.format_exc()) changed = True else: changed = False # If neither have checksums, both src and dest are directories. if checksum_src is None and checksum_dest is None: if remote_src and os.path.isdir(module.params['src']): b_src = to_bytes(module.params['src'], errors='surrogate_or_strict') b_dest = to_bytes(module.params['dest'], errors='surrogate_or_strict') if src.endswith(os.path.sep) and os.path.isdir( module.params['dest']): diff_files_changed = copy_diff_files(b_src, b_dest, module) left_only_changed = copy_left_only(b_src, b_dest, module) common_dirs_changed = copy_common_dirs(b_src, b_dest, module) owner_group_changed = chown_recursive(b_dest, module) if diff_files_changed or left_only_changed or common_dirs_changed or owner_group_changed: changed = True if src.endswith( os.path.sep) and not os.path.exists(module.params['dest']): b_basename = to_bytes(os.path.basename(src), errors='surrogate_or_strict') b_dest = to_bytes(os.path.join(b_dest, b_basename), errors='surrogate_or_strict') b_src = to_bytes(os.path.join(module.params['src'], ""), errors='surrogate_or_strict') if not module.check_mode: shutil.copytree(b_src, b_dest, symlinks=not (local_follow)) chown_recursive(dest, module) changed = True if not src.endswith(os.path.sep) and os.path.isdir( module.params['dest']): b_basename = to_bytes(os.path.basename(src), errors='surrogate_or_strict') b_dest = to_bytes(os.path.join(b_dest, b_basename), errors='surrogate_or_strict') b_src = to_bytes(os.path.join(module.params['src'], ""), errors='surrogate_or_strict') if not module.check_mode and not os.path.exists(b_dest): shutil.copytree(b_src, b_dest, symlinks=not (local_follow)) changed = True chown_recursive(dest, module) if module.check_mode and not os.path.exists(b_dest): changed = True if os.path.exists(b_dest): diff_files_changed = copy_diff_files(b_src, b_dest, module) left_only_changed = copy_left_only(b_src, b_dest, module) common_dirs_changed = copy_common_dirs( b_src, b_dest, module) owner_group_changed = chown_recursive(b_dest, module) if diff_files_changed or left_only_changed or common_dirs_changed or owner_group_changed: changed = True if not src.endswith(os.path.sep) and not os.path.exists( module.params['dest']): b_basename = to_bytes(os.path.basename(module.params['src']), errors='surrogate_or_strict') b_dest = to_bytes(os.path.join(b_dest, b_basename), errors='surrogate_or_strict') if not module.check_mode and not os.path.exists(b_dest): os.makedirs(b_dest) b_src = to_bytes(os.path.join(module.params['src'], ""), errors='surrogate_or_strict') diff_files_changed = copy_diff_files(b_src, b_dest, module) left_only_changed = copy_left_only(b_src, b_dest, module) common_dirs_changed = copy_common_dirs( b_src, b_dest, module) owner_group_changed = chown_recursive(b_dest, module) if diff_files_changed or left_only_changed or common_dirs_changed or owner_group_changed: changed = True if module.check_mode and not os.path.exists(b_dest): changed = True res_args = dict(dest=dest, src=src, md5sum=md5sum_src, checksum=checksum_src, changed=changed) if backup_file: res_args['backup_file'] = backup_file if not module.check_mode: file_args = module.load_file_common_arguments(module.params, path=dest) res_args['changed'] = module.set_fs_attributes_if_different( file_args, res_args['changed']) module.exit_json(**res_args)
def main(): module = AssibleModule( argument_spec=dict( state=dict( type='str', default='present', choices=['absent', 'build-dep', 'fixed', 'latest', 'present']), update_cache=dict(type='bool', aliases=['update-cache']), update_cache_retries=dict(type='int', default=5), update_cache_retry_max_delay=dict(type='int', default=12), cache_valid_time=dict(type='int', default=0), purge=dict(type='bool', default=False), package=dict(type='list', elements='str', aliases=['pkg', 'name']), deb=dict(type='path'), default_release=dict(type='str', aliases=['default-release']), install_recommends=dict(type='bool', aliases=['install-recommends']), force=dict(type='bool', default=False), upgrade=dict(type='str', choices=['dist', 'full', 'no', 'safe', 'yes'], default='no'), dpkg_options=dict(type='str', default=DPKG_OPTIONS), autoremove=dict(type='bool', default=False), autoclean=dict(type='bool', default=False), fail_on_autoremove=dict(type='bool', default=False), policy_rc_d=dict(type='int', default=None), only_upgrade=dict(type='bool', default=False), force_apt_get=dict(type='bool', default=False), allow_unauthenticated=dict(type='bool', default=False, aliases=['allow-unauthenticated']), ), mutually_exclusive=[['deb', 'package', 'upgrade']], required_one_of=[[ 'autoremove', 'deb', 'package', 'update_cache', 'upgrade' ]], supports_check_mode=True, ) module.run_command_environ_update = APT_ENV_VARS if not HAS_PYTHON_APT: if module.check_mode: module.fail_json( msg="%s must be installed to use check mode. " "If run normally this module can auto-install it." % PYTHON_APT) try: # We skip cache update in auto install the dependency if the # user explicitly declared it with update_cache=no. if module.params.get('update_cache') is False: module.warn( "Auto-installing missing dependency without updating cache: %s" % PYTHON_APT) else: module.warn( "Updating cache and auto-installing missing dependency: %s" % PYTHON_APT) module.run_command(['apt-get', 'update'], check_rc=True) module.run_command([ 'apt-get', 'install', '--no-install-recommends', PYTHON_APT, '-y', '-q' ], check_rc=True) global apt, apt_pkg import apt import apt.debfile import apt_pkg except ImportError: module.fail_json( msg="Could not import python modules: apt, apt_pkg. " "Please install %s package." % PYTHON_APT) global APTITUDE_CMD APTITUDE_CMD = module.get_bin_path("aptitude", False) global APT_GET_CMD APT_GET_CMD = module.get_bin_path("apt-get") p = module.params if p['upgrade'] == 'no': p['upgrade'] = None use_apt_get = p['force_apt_get'] if not use_apt_get and not APTITUDE_CMD: use_apt_get = True updated_cache = False updated_cache_time = 0 install_recommends = p['install_recommends'] allow_unauthenticated = p['allow_unauthenticated'] dpkg_options = expand_dpkg_options(p['dpkg_options']) autoremove = p['autoremove'] fail_on_autoremove = p['fail_on_autoremove'] autoclean = p['autoclean'] # Get the cache object cache = get_cache(module) try: if p['default_release']: try: apt_pkg.config['APT::Default-Release'] = p['default_release'] except AttributeError: apt_pkg.Config['APT::Default-Release'] = p['default_release'] # reopen cache w/ modified config cache.open(progress=None) mtimestamp, updated_cache_time = get_updated_cache_time() # Cache valid time is default 0, which will update the cache if # needed and `update_cache` was set to true updated_cache = False if p['update_cache'] or p['cache_valid_time']: now = datetime.datetime.now() tdelta = datetime.timedelta(seconds=p['cache_valid_time']) if not mtimestamp + tdelta >= now: # Retry to update the cache with exponential backoff err = '' update_cache_retries = module.params.get( 'update_cache_retries') update_cache_retry_max_delay = module.params.get( 'update_cache_retry_max_delay') randomize = random.randint(0, 1000) / 1000.0 for retry in range(update_cache_retries): try: cache.update() break except apt.cache.FetchFailedException as e: err = to_native(e) # Use exponential backoff plus a little bit of randomness delay = 2**retry + randomize if delay > update_cache_retry_max_delay: delay = update_cache_retry_max_delay + randomize time.sleep(delay) else: module.fail_json(msg='Failed to update apt cache: %s' % (err if err else 'unknown reason')) cache.open(progress=None) mtimestamp, post_cache_update_time = get_updated_cache_time() if updated_cache_time != post_cache_update_time: updated_cache = True updated_cache_time = post_cache_update_time # If there is nothing else to do exit. This will set state as # changed based on if the cache was updated. if not p['package'] and not p['upgrade'] and not p['deb']: module.exit_json(changed=updated_cache, cache_updated=updated_cache, cache_update_time=updated_cache_time) force_yes = p['force'] if p['upgrade']: upgrade(module, p['upgrade'], force_yes, p['default_release'], use_apt_get, dpkg_options, autoremove, fail_on_autoremove, allow_unauthenticated) if p['deb']: if p['state'] != 'present': module.fail_json(msg="deb only supports state=present") if '://' in p['deb']: p['deb'] = fetch_file(module, p['deb']) install_deb(module, p['deb'], cache, install_recommends=install_recommends, allow_unauthenticated=allow_unauthenticated, force=force_yes, fail_on_autoremove=fail_on_autoremove, dpkg_options=p['dpkg_options']) unfiltered_packages = p['package'] or () packages = [ package.strip() for package in unfiltered_packages if package != '*' ] all_installed = '*' in unfiltered_packages latest = p['state'] == 'latest' if latest and all_installed: if packages: module.fail_json( msg= 'unable to install additional packages when upgrading all installed packages' ) upgrade(module, 'yes', force_yes, p['default_release'], use_apt_get, dpkg_options, autoremove, fail_on_autoremove, allow_unauthenticated) if packages: for package in packages: if package.count('=') > 1: module.fail_json(msg="invalid package spec: %s" % package) if latest and '=' in package: module.fail_json( msg='version number inconsistent with state=latest: %s' % package) if not packages: if autoclean: cleanup(module, p['purge'], force=force_yes, operation='autoclean', dpkg_options=dpkg_options) if autoremove: cleanup(module, p['purge'], force=force_yes, operation='autoremove', dpkg_options=dpkg_options) if p['state'] in ('latest', 'present', 'build-dep', 'fixed'): state_upgrade = False state_builddep = False state_fixed = False if p['state'] == 'latest': state_upgrade = True if p['state'] == 'build-dep': state_builddep = True if p['state'] == 'fixed': state_fixed = True success, retvals = install( module, packages, cache, upgrade=state_upgrade, default_release=p['default_release'], install_recommends=install_recommends, force=force_yes, dpkg_options=dpkg_options, build_dep=state_builddep, fixed=state_fixed, autoremove=autoremove, fail_on_autoremove=fail_on_autoremove, only_upgrade=p['only_upgrade'], allow_unauthenticated=allow_unauthenticated) # Store if the cache has been updated retvals['cache_updated'] = updated_cache # Store when the update time was last retvals['cache_update_time'] = updated_cache_time if success: module.exit_json(**retvals) else: module.fail_json(**retvals) elif p['state'] == 'absent': remove(module, packages, cache, p['purge'], force=force_yes, dpkg_options=dpkg_options, autoremove=autoremove) except apt.cache.LockFailedException as lockFailedException: module.fail_json(msg="Failed to lock apt for exclusive operation: %s" % lockFailedException) except apt.cache.FetchFailedException as fetchFailedException: module.fail_json(msg="Could not fetch updated apt files: %s" % fetchFailedException)
def run_module(): module_args = dict( server=dict(type='str', required=True), token=dict(type='str'), collections=dict( type='list', elements='dict', required=True, options=dict( namespace=dict(type='str', required=True), name=dict(type='str', required=True), version=dict(type='str', default='1.0.0'), dependencies=dict(type='dict', default={}), use_symlink=dict(type='bool', default=False), ), ), ) module = AssibleModule(argument_spec=module_args, supports_check_mode=False) result = dict(changed=True, results=[]) for idx, collection in enumerate(module.params['collections']): collection_dir = os.path.join( module.tmpdir, "%s-%s-%s" % (collection['namespace'], collection['name'], collection['version'])) b_collection_dir = to_bytes(collection_dir, errors='surrogate_or_strict') os.mkdir(b_collection_dir) with open(os.path.join(b_collection_dir, b'README.md'), mode='wb') as fd: fd.write(b"Collection readme") galaxy_meta = { 'namespace': collection['namespace'], 'name': collection['name'], 'version': collection['version'], 'readme': 'README.md', 'authors': ['Collection author <*****@*****.**'], 'dependencies': collection['dependencies'], 'license': ['GPL-3.0-or-later'], } with open(os.path.join(b_collection_dir, b'galaxy.yml'), mode='wb') as fd: fd.write( to_bytes(yaml.safe_dump(galaxy_meta), errors='surrogate_or_strict')) with tempfile.NamedTemporaryFile(mode='wb') as temp_fd: temp_fd.write(b"data") if collection['use_symlink']: os.mkdir(os.path.join(b_collection_dir, b'docs')) os.mkdir(os.path.join(b_collection_dir, b'plugins')) b_target_file = b'RE\xc3\x85DM\xc3\x88.md' with open(os.path.join(b_collection_dir, b_target_file), mode='wb') as fd: fd.write(b'data') os.symlink( b_target_file, os.path.join(b_collection_dir, b_target_file + b'-link')) os.symlink( temp_fd.name, os.path.join(b_collection_dir, b_target_file + b'-outside-link')) os.symlink( os.path.join(b'..', b_target_file), os.path.join(b_collection_dir, b'docs', b_target_file)) os.symlink( os.path.join(b_collection_dir, b_target_file), os.path.join(b_collection_dir, b'plugins', b_target_file)) os.symlink(b'docs', os.path.join(b_collection_dir, b'docs-link')) release_filename = '%s-%s-%s.tar.gz' % (collection['namespace'], collection['name'], collection['version']) collection_path = os.path.join(collection_dir, release_filename) rc, stdout, stderr = module.run_command( ['assible-galaxy', 'collection', 'build'], cwd=collection_dir) result['results'].append( {'build': { 'rc': rc, 'stdout': stdout, 'stderr': stderr, }}) # To save on time, skip the import wait until the last collection is being uploaded. publish_args = [ 'assible-galaxy', 'collection', 'publish', collection_path, '--server', module.params['server'] ] if module.params['token']: publish_args.extend(['--token', module.params['token']]) if idx != (len(module.params['collections']) - 1): publish_args.append('--no-wait') rc, stdout, stderr = module.run_command(publish_args) result['results'][-1]['publish'] = { 'rc': rc, 'stdout': stdout, 'stderr': stderr, } failed = bool( sum(r['build']['rc'] + r['publish']['rc'] for r in result['results'])) module.exit_json(failed=failed, **result)
def main(): module = AssibleModule( argument_spec=dict( path=dict(type='path', required=True, aliases=['dest', 'name']), follow=dict(type='bool', default=False), get_md5=dict(type='bool', default=False), get_checksum=dict(type='bool', default=True), get_mime=dict(type='bool', default=True, aliases=['mime', 'mime_type', 'mime-type']), get_attributes=dict(type='bool', default=True, aliases=['attr', 'attributes']), checksum_algorithm=dict(type='str', default='sha1', choices=[ 'md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512' ], aliases=['checksum', 'checksum_algo']), ), supports_check_mode=True, ) path = module.params.get('path') b_path = to_bytes(path, errors='surrogate_or_strict') follow = module.params.get('follow') get_mime = module.params.get('get_mime') get_attr = module.params.get('get_attributes') get_checksum = module.params.get('get_checksum') checksum_algorithm = module.params.get('checksum_algorithm') # NOTE: undocumented option since 2.9 to be removed at a later date if possible (3.0+) # no real reason for keeping other than fear we may break older content. get_md5 = module.params.get('get_md5') # main stat data try: if follow: st = os.stat(b_path) else: st = os.lstat(b_path) except OSError as e: if e.errno == errno.ENOENT: output = {'exists': False} module.exit_json(changed=False, stat=output) module.fail_json(msg=e.strerror) # process base results output = format_output(module, path, st) # resolved permissions for perm in [('readable', os.R_OK), ('writeable', os.W_OK), ('executable', os.X_OK)]: output[perm[0]] = os.access(b_path, perm[1]) # symlink info if output.get('islnk'): output['lnk_source'] = os.path.realpath(b_path) output['lnk_target'] = os.readlink(b_path) try: # user data pw = pwd.getpwuid(st.st_uid) output['pw_name'] = pw.pw_name except (TypeError, KeyError): pass try: # group data grp_info = grp.getgrgid(st.st_gid) output['gr_name'] = grp_info.gr_name except (KeyError, ValueError, OverflowError): pass # checksums if output.get('isreg') and output.get('readable'): # NOTE: see above about get_md5 if get_md5: # Will fail on FIPS-140 compliant systems try: output['md5'] = module.md5(b_path) except ValueError: output['md5'] = None if get_checksum: output['checksum'] = module.digest_from_file( b_path, checksum_algorithm) # try to get mime data if requested if get_mime: output['mimetype'] = output['charset'] = 'unknown' mimecmd = module.get_bin_path('file') if mimecmd: mimecmd = [mimecmd, '--mime-type', '--mime-encoding', b_path] try: rc, out, err = module.run_command(mimecmd) if rc == 0: mimetype, charset = out.rsplit(':', 1)[1].split(';') output['mimetype'] = mimetype.strip() output['charset'] = charset.split('=')[1].strip() except Exception: pass # try to get attr data if get_attr: output['version'] = None output['attributes'] = [] output['attr_flags'] = '' out = module.get_file_attributes(b_path) for x in ('version', 'attributes', 'attr_flags'): if x in out: output[x] = out[x] module.exit_json(changed=False, stat=output)