def render(jp_data, saltenv='base', sls='', **kws): ''' Accepts Java Properties as a string or as a file object and runs it through the jproperties parser. Uses the jproperties package https://pypi.python.org/pypi/jproperties so please "pip install jproperties" to use this renderer. Returns a flat dictionary by default: {'some.java.thing': 'whatever'} If using a 'shebang' "#!jproperties" header on the first line, an argument can be optionally supplied as a key to contain a dictionary of the rendered properties (ie. "#!jproperties foo"): {'foo': {'some.java.thing': 'whatever'}} :rtype: A Python data structure ''' if not isinstance(jp_data, string_types): jp_data = jp_data.read() container = False if jp_data.startswith('#!'): args = jp_data[:jp_data.find('\n')].split() if len(args) >= 2: container = args[1] jp_data = jp_data[(jp_data.find('\n') + 1):] if not jp_data.strip(): return {} properties = jp() properties.load(jp_data) if container: return {container: dict([(k, properties[k]) for k in six.iterkeys(properties)])} else: return dict([(k, properties[k]) for k in six.iterkeys(properties)])
def test_basic(self): ''' Make sure that the process is alive 2s later ''' def spin(): salt.utils.appendproctitle('test_basic') while True: time.sleep(1) process_manager = salt.utils.process.ProcessManager() process_manager.add_process(spin) initial_pid = next(six.iterkeys(process_manager._process_map)) time.sleep(2) process_manager.check_children() try: assert initial_pid == next(six.iterkeys(process_manager._process_map)) finally: process_manager.stop_restarting() process_manager.kill_children() time.sleep(0.5) # Are there child processes still running? if process_manager._process_map.keys(): process_manager.send_signal_to_processes(signal.SIGKILL) process_manager.stop_restarting() process_manager.kill_children()
def table_find(table_to_find): ''' Finds the schema in which the given table is present CLI Example:: salt '*' drizzle.table_find table_name ''' # Initializing the required variables ret_val = {} count = 1 drizzle_db = _connect() cursor = drizzle_db.cursor() # Finding the schema schema = schemas() for schema_iter in six.iterkeys(schema): table = tables(schema[schema_iter]) for table_iter in six.iterkeys(table): if table[table_iter] == table_to_find: ret_val[count] = schema[schema_iter] count = count+1 cursor.close() drizzle_db.close() return ret_val
def __call_cli(jboss_config, command, retries=1): command_segments = [ jboss_config['cli_path'], '--connect', '--controller="{0}"'.format(jboss_config['controller']) ] if 'cli_user' in six.iterkeys(jboss_config): command_segments.append('--user="******"'.format(jboss_config['cli_user'])) if 'cli_password' in six.iterkeys(jboss_config): command_segments.append('--password="******"'.format(jboss_config['cli_password'])) command_segments.append('--command="{0}"'.format(__escape_command(command))) cli_script = ' '.join(command_segments) cli_command_result = __salt__['cmd.run_all'](cli_script) log.debug('cli_command_result=%s', str(cli_command_result)) log.debug('========= STDOUT:\n%s', cli_command_result['stdout']) log.debug('========= STDERR:\n%s', cli_command_result['stderr']) log.debug('========= RETCODE: %d', cli_command_result['retcode']) if cli_command_result['retcode'] == 127: raise CommandExecutionError('Could not execute jboss-cli.sh script. Have you specified server_dir variable correctly?\nCurrent CLI path: {cli_path}. '.format(cli_path=jboss_config['cli_path'])) if cli_command_result['retcode'] == 1 and 'Unable to authenticate against controller' in cli_command_result['stderr']: raise CommandExecutionError('Could not authenticate against controller, please check username and password for the management console. Err code: {retcode}, stdout: {stdout}, stderr: {stderr}'.format(**cli_command_result)) # It may happen that eventhough server is up it may not respond to the call if cli_command_result['retcode'] == 1 and 'JBAS012144' in cli_command_result['stderr'] and retries > 0: # Cannot connect to cli log.debug('Command failed, retrying... (%d tries left)', retries) time.sleep(3) return __call_cli(jboss_config, command, retries - 1) return cli_command_result
def stages_iter(self): ''' Return an iterator that yields the state call data as it is processed ''' def yielder(gen_ret): if (not isinstance(gen_ret, list) and not isinstance(gen_ret, dict) and hasattr(gen_ret, 'next')): for sub_ret in gen_ret: for yret in yielder(sub_ret): yield yret else: yield gen_ret self.over_run = {} yield self.over for comp in self.over: name = next(six.iterkeys(comp)) stage = comp[name] if name not in self.over_run: v_stage = self.verify_stage(stage) if isinstance(v_stage, list): yield [comp] yield v_stage else: for sret in self.call_stage(name, stage): for yret in yielder(sret): sname = next(six.iterkeys(yret)) yield [self.get_stage(sname)] final = {} for minion in yret[sname]: final[minion] = yret[sname][minion]['ret'] yield final
def test_cmd_call(self): result = self.HIGHSTATE.state.call_template_str( textwrap.dedent( """\ #!pydsl state('A').cmd.run('echo this is state A', cwd='/') some_var = 12345 def do_something(a, b, *args, **kws): return dict(result=True, changes={'a': a, 'b': b, 'args': args, 'kws': kws, 'some_var': some_var}) state('C').cmd.call(do_something, 1, 2, 3, x=1, y=2) \ .require(state('A').cmd) state('G').cmd.wait('echo this is state G', cwd='/') \ .watch(state('C').cmd) """ ) ) ret = next(result[k] for k in six.iterkeys(result) if "do_something" in k) changes = ret["changes"] self.assertEqual(changes, dict(a=1, b=2, args=(3,), kws=dict(x=1, y=2), some_var=12345)) ret = next(result[k] for k in six.iterkeys(result) if "-G_" in k) self.assertEqual(ret["changes"]["stdout"], "this is state G")
def list_upgrades(jid, style="group", outputter="nested", ext_source=None): """ Show list of available pkg upgrades using a specified format style CLI Example: .. code-block:: bash salt-run pkg.list_upgrades jid=20141120114114417719 style=group """ mminion = salt.minion.MasterMinion(__opts__) returner = _get_returner((__opts__["ext_job_cache"], ext_source, __opts__["master_job_cache"])) data = mminion.returners["{0}.get_jid".format(returner)](jid) pkgs = {} if style == "group": for minion in data: results = data[minion]["return"] for pkg, pkgver in six.iteritems(results): if pkg not in six.iterkeys(pkgs): pkgs[pkg] = {pkgver: {"hosts": []}} if pkgver not in six.iterkeys(pkgs[pkg]): pkgs[pkg].update({pkgver: {"hosts": []}}) pkgs[pkg][pkgver]["hosts"].append(minion) if outputter: salt.output.display_output(pkgs, outputter, opts=__opts__) return pkgs
def test_kill(self): def spin(): salt.utils.appendproctitle('test_kill') while True: time.sleep(1) process_manager = salt.utils.process.ProcessManager() process_manager.add_process(spin) initial_pid = next(six.iterkeys(process_manager._process_map)) # kill the child os.kill(initial_pid, signal.SIGKILL) # give the OS time to give the signal... time.sleep(0.1) process_manager.check_children() try: assert initial_pid != next(six.iterkeys(process_manager._process_map)) finally: process_manager.stop_restarting() process_manager.kill_children() time.sleep(0.5) # Are there child processes still running? if process_manager._process_map.keys(): process_manager.send_signal_to_processes(signal.SIGKILL) process_manager.stop_restarting() process_manager.kill_children()
def SetIncludes(self, includes): if includes: for inc in includes: value = inc[next(six.iterkeys(inc))] include = next(six.iterkeys(inc)) self.SetInclude(include, value) log.debug('was asked to set {0} to {1}'.format(include, value))
def user_role_add(user_id=None, user=None, tenant_id=None, tenant=None, role_id=None, role=None, profile=None, project_id=None, project_name=None, **connection_args): ''' Add role for user in tenant (keystone user-role-add) CLI Examples: .. code-block:: bash salt '*' keystone.user_role_add \ user_id=298ce377245c4ec9b70e1c639c89e654 \ tenant_id=7167a092ece84bae8cead4bf9d15bb3b \ role_id=ce377245c4ec9b70e1c639c89e8cead4 salt '*' keystone.user_role_add user=admin tenant=admin role=admin ''' kstone = auth(profile, **connection_args) if project_id and not tenant_id: tenant_id = project_id elif project_name and not tenant: tenant = project_name if user: user_id = user_get(name=user, profile=profile, **connection_args)[user].get('id') else: user = next(six.iterkeys(user_get(user_id, profile=profile, **connection_args)))['name'] if not user_id: return {'Error': 'Unable to resolve user id'} if tenant: tenant_id = tenant_get(name=tenant, profile=profile, **connection_args)[tenant].get('id') else: tenant = next(six.iterkeys(tenant_get(tenant_id, profile=profile, **connection_args)))['name'] if not tenant_id: return {'Error': 'Unable to resolve tenant/project id'} if role: role_id = role_get(name=role, profile=profile, **connection_args)[role]['id'] else: role = next(six.iterkeys(role_get(role_id, profile=profile, **connection_args)))['name'] if not role_id: return {'Error': 'Unable to resolve role id'} if _OS_IDENTITY_API_VERSION > 2: kstone.roles.grant(role_id, user=user_id, project=tenant_id) else: kstone.roles.add_user_role(user_id, role_id, tenant_id) ret_msg = '"{0}" role added for user "{1}" for "{2}" tenant/project' return ret_msg.format(role, user, tenant)
def test_restarting(self): ''' Make sure that the process is alive 2s later ''' def die(): time.sleep(1) process_manager = salt.utils.process.ProcessManager() process_manager.add_process(die) initial_pid = next(six.iterkeys(process_manager._process_map)) time.sleep(2) process_manager.check_children() assert initial_pid != next(six.iterkeys(process_manager._process_map)) process_manager.kill_children()
def test_kill(self): def spin(): while True: time.sleep(1) process_manager = salt.utils.process.ProcessManager() process_manager.add_process(spin) initial_pid = next(six.iterkeys(process_manager._process_map)) # kill the child os.kill(initial_pid, signal.SIGTERM) # give the OS time to give the signal... time.sleep(0.1) process_manager.check_children() assert initial_pid != next(six.iterkeys(process_manager._process_map)) process_manager.kill_children()
def test_basic(self): ''' Make sure that the process is alive 2s later ''' def spin(): while True: time.sleep(1) process_manager = salt.utils.process.ProcessManager() process_manager.add_process(spin) initial_pid = next(six.iterkeys(process_manager._process_map)) time.sleep(2) process_manager.check_children() assert initial_pid == next(six.iterkeys(process_manager._process_map)) process_manager.kill_children()
def _list_iter(host=None, path=None): ''' Return a generator iterating over hosts path path to the container parent default: /var/lib/lxc (system default) .. versionadded:: Beryllium ''' tgt = host or '*' client = salt.client.get_local_client(__opts__['conf_file']) for container_info in client.cmd_iter( tgt, 'lxc.list', kwarg={'path': path} ): if not container_info: continue if not isinstance(container_info, dict): continue chunk = {} id_ = next(six.iterkeys(container_info)) if host and host != id_: continue if not isinstance(container_info[id_], dict): continue if 'ret' not in container_info[id_]: continue if not isinstance(container_info[id_]['ret'], dict): continue chunk[id_] = container_info[id_]['ret'] yield chunk
def resume(name): ''' Resume a paused vm ''' ret = {} client = salt.client.get_local_client(__opts__['conf_file']) data = vm_info(name, quiet=True) if not data: __jid_event__.fire_event({'error': 'Failed to find VM {0} to pause'.format(name)}, 'progress') return 'not found' hyper = next(six.iterkeys(data)) if data[hyper][name]['state'] != 'paused': __jid_event__.fire_event({'error': 'VM {0} is not paused'.format(name)}, 'progress') return 'bad state' try: cmd_ret = client.cmd_iter( hyper, 'virt.resume', [name], timeout=600) except SaltClientError as client_error: return 'Virtual machine {0} could not be resumed: {1}'.format(name, client_error) for comp in cmd_ret: ret.update(comp) __jid_event__.fire_event({'message': 'Resumed VM {0}'.format(name)}, 'progress') return 'good'
def config(name, config, edit=True): ''' Create VirtualHost configuration files name File for the virtual host config VirtualHost configurations .. note:: This function is not meant to be used from the command line. Config is meant to be an ordered dict of all of the apache configs. CLI Example: .. code-block:: bash salt '*' apache.config /etc/httpd/conf.d/ports.conf config="[{'Listen': '22'}]" ''' for entry in config: key = next(six.iterkeys(entry)) configs = _parse_config(entry[key], key) if edit: with salt.utils.fopen(name, 'w') as configfile: configfile.write('# This file is managed by saltstack.\n') configfile.write(configs) return configs
def _expand_one_key_dictionary(_dict): ''' Returns the only one key and it's value from a dictionary. ''' key = next(six.iterkeys(_dict)) value = _dict[key] return key, value
def attrs(self): kwargs = self.kwargs # handle our requisites for attr in REQUISITES: if attr in kwargs: # our requisites should all be lists, but when you only have a # single item it's more convenient to provide it without # wrapping it in a list. transform them into a list if not isinstance(kwargs[attr], list): kwargs[attr] = [kwargs[attr]] # rebuild the requisite list transforming any of the actual # StateRequisite objects into their representative dict kwargs[attr] = [ req() if isinstance(req, StateRequisite) else req for req in kwargs[attr] ] # build our attrs from kwargs. we sort the kwargs by key so that we # have consistent ordering for tests return [ {k: kwargs[k]} for k in sorted(six.iterkeys(kwargs)) ]
def spec_check(self, auth_list, fun, form): ''' Check special API permissions ''' if form != 'cloud': comps = fun.split('.') if len(comps) != 2: return False mod = comps[0] fun = comps[1] else: mod = fun for ind in auth_list: if isinstance(ind, six.string_types): if ind.startswith('@') and ind[1:] == mod: return True if ind == '@{0}'.format(form): return True if ind == '@{0}s'.format(form): return True elif isinstance(ind, dict): if len(ind) != 1: continue valid = next(six.iterkeys(ind)) if valid.startswith('@') and valid[1:] == mod: if isinstance(ind[valid], six.string_types): if self.match_check(ind[valid], fun): return True elif isinstance(ind[valid], list): for regex in ind[valid]: if self.match_check(regex, fun): return True return False
def __gather_minions(self): ''' Return a list of minions to use for the batch run ''' args = [self.opts['tgt'], 'test.ping', [], self.opts['timeout'], ] selected_target_option = self.opts.get('selected_target_option', None) if selected_target_option is not None: args.append(selected_target_option) else: args.append(self.opts.get('expr_form', 'glob')) ping_gen = self.local.cmd_iter(*args, **self.eauth) fret = set() try: for ret in ping_gen: m = next(six.iterkeys(ret)) if m is not None: fret.add(m) return (list(fret), ping_gen) except StopIteration: raise salt.exceptions.SaltClientError('No minions matched the target.')
def destroy(self, linger=5000): if self.cpub is True and self.sub.closed is False: # Wait at most 2.5 secs to send any remaining messages in the # socket or the context.term() below will hang indefinitely. # See https://github.com/zeromq/pyzmq/issues/102 self.sub.close() if self.cpush is True and self.push.closed is False: self.push.close() # If sockets are not unregistered from a poller, nothing which touches # that poller gets garbage collected. The Poller itself, its # registered sockets and the Context if isinstance(self.poller.sockets, dict): for socket in six.iterkeys(self.poller.sockets): if socket.closed is False: socket.setsockopt(zmq.LINGER, linger) socket.close() self.poller.unregister(socket) else: for socket in self.poller.sockets: if socket[0].closed is False: socket[0].setsockopt(zmq.LINGER, linger) socket[0].close() self.poller.unregister(socket[0]) if self.context.closed is False: self.context.term() # Hardcore destruction if hasattr(self.context, 'destroy'): self.context.destroy(linger=1) # https://github.com/zeromq/pyzmq/issues/173#issuecomment-4037083 # Assertion failed: get_load () == 0 (poller_base.cpp:32) time.sleep(0.025)
def _sanatize_network_params(self, kwargs): """ Sanatize novaclient network parameters """ params = [ "label", "bridge", "bridge_interface", "cidr", "cidr_v6", "dns1", "dns2", "fixed_cidr", "gateway", "gateway_v6", "multi_host", "priority", "project_id", "vlan_start", "vpn_start", ] for variable in six.iterkeys(kwargs): # iterate over a copy, we might delete some if variable not in params: del kwargs[variable] return kwargs
def purge(name, delete_key=True): ''' Destroy the named vm ''' ret = {} client = salt.client.get_local_client(__opts__['conf_file']) data = vm_info(name, quiet=True) if not data: __jid_event__.fire_event({'error': 'Failed to find vm {0} to purge'.format(name)}, 'progress') return 'fail' hyper = next(six.iterkeys(data)) try: cmd_ret = client.cmd_iter( hyper, 'virt.purge', [name, True], timeout=600) except SaltClientError as client_error: return 'Virtual machine {0} could not be purged: {1}'.format(name, client_error) for comp in cmd_ret: ret.update(comp) if delete_key: skey = salt.key.Key(__opts__) skey.delete_key(name) __jid_event__.fire_event({'message': 'Purged VM {0}'.format(name)}, 'progress') return 'good'
def force_off(name): ''' Force power down the named virtual machine ''' ret = {} client = salt.client.get_local_client(__opts__['conf_file']) data = vm_info(name, quiet=True) if not data: print('Failed to find vm {0} to destroy'.format(name)) return 'fail' hyper = next(six.iterkeys(data)) if data[hyper][name]['state'] == 'shutdown': print('VM {0} is already shutdown'.format(name)) return'bad state' try: cmd_ret = client.cmd_iter( hyper, 'virt.destroy', [name], timeout=600) except SaltClientError as client_error: return 'Virtual machine {0} could not be forced off: {1}'.format(name, client_error) for comp in cmd_ret: ret.update(comp) __jid_event__.fire_event({'message': 'Powered off VM {0}'.format(name)}, 'progress') return 'good'
def list_keypairs(call=None): ''' Return a dict of all available VM locations on the cloud provider with relevant data ''' if call != 'function': log.error( 'The list_keypairs function must be called with -f or --function.' ) return False items = query(method='account/keys') ret = {} for key_pair in items['ssh_keys']: name = key_pair['name'] if name in ret: raise SaltCloudSystemExit( 'A duplicate key pair name, \'{0}\', was found in DigitalOcean\'s ' 'key pair list. Please change the key name stored by DigitalOcean. ' 'Be sure to adjust the value of \'ssh_key_file\' in your cloud ' 'profile or provider configuration, if necessary.'.format( name ) ) ret[name] = {} for item in six.iterkeys(key_pair): ret[name][item] = str(key_pair[item]) return ret
def list_renderers(*args): """ List the renderers loaded on the minion .. versionadded:: 2015.5.0 CLI Example: .. code-block:: bash salt '*' sys.list_renderers Render names can be specified as globs. .. code-block:: bash salt '*' sys.list_renderers 'yaml*' """ ren_ = salt.loader.render(__opts__, []) ren = set() if not args: for func in six.iterkeys(ren_): ren.add(func) return sorted(ren) for module in args: for func in fnmatch.filter(ren_, module): ren.add(func) return sorted(ren)
def avail_images(call=None): ''' Return a list of the images that are on the provider ''' if call == 'action': raise SaltCloudSystemExit( 'The avail_images function must be called with ' '-f or --function, or with the --list-images option' ) fetch = True page = 1 ret = {} while fetch: items = query(method='images', command='?page=' + str(page) + '&per_page=200') for image in items['images']: ret[image['name']] = {} for item in six.iterkeys(image): ret[image['name']][item] = image[item] page += 1 try: fetch = 'next' in items['links']['pages'] except KeyError: fetch = False return ret
def pause(name): ''' Pause the named VM ''' ret = {} client = salt.client.get_local_client(__opts__['conf_file']) data = vm_info(name, quiet=True) if not data: __jid_event__.fire_event({'error': 'Failed to find VM {0} to pause'.format(name)}, 'progress') return 'fail' host = next(six.iterkeys(data)) if data[host][name]['state'] == 'paused': __jid_event__.fire_event({'error': 'VM {0} is already paused'.format(name)}, 'progress') return 'bad state' try: cmd_ret = client.cmd_iter( host, 'virt.pause', [name], timeout=600) except SaltClientError as client_error: return 'Virtual machine {0} could not be pasued: {1}'.format(name, client_error) for comp in cmd_ret: ret.update(comp) __jid_event__.fire_event({'message': 'Paused VM {0}'.format(name)}, 'progress') return 'good'
def start(name): ''' Start a named virtual machine ''' ret = {} client = salt.client.get_local_client(__opts__['conf_file']) data = vm_info(name, quiet=True) if not data: __jid_event__.fire_event({'message': 'Failed to find vm {0} to start'.format(name)}, 'progress') return 'fail' hyper = next(six.iterkeys(data)) if data[hyper][name]['state'] == 'running': print('VM {0} is already running'.format(name)) return 'bad state' try: cmd_ret = client.cmd_iter( hyper, 'virt.start', [name], timeout=600) except SaltClientError as client_error: return 'Virtual machine {0} not started: {1}'. format(name, client_error) for comp in cmd_ret: ret.update(comp) __jid_event__.fire_event({'message': 'Started VM {0}'.format(name)}, 'progress') return 'good'
def _execute_pillar(pillar_name, run_type): ''' Run one or more nagios plugins from pillar data and get the result of run_type The pillar have to be in this format: ------ webserver: Ping_google: - check_icmp: 8.8.8.8 - check_icmp: google.com Load: - check_load: -w 0.8 -c 1 APT: - check_apt ------- ''' groups = __salt__['pillar.get'](pillar_name) data = {} for group in groups: data[group] = {} commands = groups[group] for command in commands: # Check if is a dict to get the arguments # in command if not set the arguments to empty string if isinstance(command, dict): plugin = next(six.iterkeys(command)) args = command[plugin] else: plugin = command args = '' command_key = _format_dict_key(args, plugin) data[group][command_key] = run_type(plugin, args) return data
def test_get(self): # test with no JID self.http_client.fetch( self.get_url('/jobs'), self.stop, method='GET', headers={saltnado.AUTH_TOKEN_HEADER: self.token['token']}, follow_redirects=False, ) response = self.wait(timeout=30) response_obj = json.loads(response.body)['return'][0] for jid, ret in six.iteritems(response_obj): self.assertIn('Function', ret) self.assertIn('Target', ret) self.assertIn('Target-type', ret) self.assertIn('User', ret) self.assertIn('StartTime', ret) self.assertIn('Arguments', ret) # test with a specific JID passed in jid = next(six.iterkeys(response_obj)) self.http_client.fetch( self.get_url('/jobs/{0}'.format(jid)), self.stop, method='GET', headers={saltnado.AUTH_TOKEN_HEADER: self.token['token']}, follow_redirects=False, ) response = self.wait(timeout=30) response_obj = json.loads(response.body)['return'][0] self.assertIn('Function', response_obj) self.assertIn('Target', response_obj) self.assertIn('Target-type', response_obj) self.assertIn('User', response_obj) self.assertIn('StartTime', response_obj) self.assertIn('Arguments', response_obj) self.assertIn('Result', response_obj)
def add_job(self, data, persist=True): ''' Adds a new job to the scheduler. The format is the same as required in the configuration file. See the docs on how YAML is interpreted into python data-structures to make sure, you pass correct dictionaries. ''' # we don't do any checking here besides making sure its a dict. # eval() already does for us and raises errors accordingly if not isinstance(data, dict): raise ValueError('Scheduled jobs have to be of type dict.') if not len(data) == 1: raise ValueError('You can only schedule one new job at a time.') # if enabled is not included in the job, # assume job is enabled. for job in data.keys(): if 'enabled' not in data[job]: data[job]['enabled'] = True new_job = next(six.iterkeys(data)) if new_job in self.opts['schedule']: log.info('Updating job settings for scheduled ' 'job: {0}'.format(new_job)) else: log.info('Added new job {0} to scheduler'.format(new_job)) self.opts['schedule'].update(data) # Fire the complete event back along with updated list of schedule evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) evt.fire_event({'complete': True, 'schedule': self.opts['schedule']}, tag='/salt/minion/minion_schedule_add_complete') if persist: self.persist()
def __gather_minions(self): ''' Return a list of minions to use for the batch run ''' args = [self.opts['tgt'], 'test.ping', [], self.opts['timeout'], ] selected_target_option = self.opts.get('selected_target_option', None) if selected_target_option is not None: args.append(selected_target_option) else: args.append(self.opts.get('expr_form', 'glob')) self.pub_kwargs['yield_pub_data'] = True ping_gen = self.local.cmd_iter(*args, **self.pub_kwargs) # Broadcast to targets fret = set() nret = set() try: for ret in ping_gen: if ('minions' and 'jid') in ret: for minion in ret['minions']: nret.add(minion) continue else: m = next(six.iterkeys(ret)) if m is not None: fret.add(m) return (list(fret), ping_gen, nret.difference(fret)) except StopIteration: if not self.quiet: print_cli('No minions matched the target.') return list(fret), ping_gen
def list_returners(*args): ''' .. versionadded:: 2014.7.0 List the runners loaded on the minion CLI Example: .. code-block:: bash salt '*' sys.list_returners .. versionadded:: Lithium Returner names can be specified as globs. salt '*' sys.list_returners 's*' ''' returners_ = salt.loader.returners(__opts__, []) returners = set() if not args: for func in six.iterkeys(returners_): comps = func.split('.') if len(comps) < 2: continue returners.add(comps[0]) return sorted(returners) for module in args: for func in fnmatch.filter(returners_.keys(), module): # pylint: disable=incompatible-py3-code comps = func.split('.') # There's no problem feeding fnmatch.filter() if len(comps) < 2: # with a Py3's dict_keys() instance continue returners.add(comps[0]) return sorted(returners)
def listen(opts): """ Attach to the pub socket and grab messages """ event = salt.utils.event.get_event( opts["node"], sock_dir=opts["sock_dir"], transport=opts["transport"], opts=opts, listen=True, ) check_access_and_print_warning(opts["sock_dir"]) print(event.puburi) jid_counter = 0 found_minions = [] while True: ret = event.get_event(full=True) if ret is None: continue if opts["func_count"]: data = ret.get("data", False) if data: if ("id" in six.iterkeys(data) and data.get("id", False) not in found_minions): if data["fun"] == opts["func_count"]: jid_counter += 1 found_minions.append(data["id"]) print( "Reply received from [{0}]. Total replies now: [{1}]." .format(ret["data"]["id"], jid_counter)) continue else: print("Event fired at {0}".format(time.asctime())) print("*" * 25) print("Tag: {0}".format(ret["tag"])) print("Data:") pprint.pprint(ret["data"])
def reformat_node(item=None, full=False): ''' Reformat the returned data from joyent, determine public/private IPs and strip out fields if necessary to provide either full or brief content. :param item: node dictionary :param full: full or brief output :return: dict ''' desired_keys = [ 'id', 'name', 'state', 'public_ips', 'private_ips', 'size', 'image', 'location' ] item['private_ips'] = [] item['public_ips'] = [] if 'ips' in item: for ip in item['ips']: if is_public_ip(ip): item['public_ips'].append(ip) else: item['private_ips'].append(ip) # add any undefined desired keys for key in desired_keys: if key not in item: item[key] = None # remove all the extra key value pairs to provide a brief listing if not full: for key in six.iterkeys(item): # iterate over a copy of the keys if key not in desired_keys: del item[key] if 'state' in item: item['state'] = joyent_node_state(item['state']) return item
def list_floating_ips(call=None): ''' Return a list of the floating ips that are on the provider .. versionadded:: 2016.3.0 CLI Examples: ... code-block:: bash salt-cloud -f list_floating_ips my-digitalocean-config ''' if call == 'action': raise SaltCloudSystemExit( 'The list_floating_ips function must be called with ' '-f or --function, or with the --list-floating-ips option') fetch = True page = 1 ret = {} while fetch: items = query(method='floating_ips', command='?page=' + str(page) + '&per_page=200') for floating_ip in items['floating_ips']: ret[floating_ip['ip']] = {} for item in six.iterkeys(floating_ip): ret[floating_ip['ip']][item] = floating_ip[item] page += 1 try: fetch = 'next' in items['links']['pages'] except KeyError: fetch = False return ret
def query(hyper=None, quiet=False): ''' Query the virtual machines. When called without options all hypervisors are detected and a full query is returned. A single hypervisor can be passed in to specify an individual hypervisor to query. ''' if quiet: log.warn('\'quiet\' is deprecated. Please migrate to --quiet') ret = {} client = salt.client.get_local_client(__opts__['conf_file']) for info in client.cmd_iter('virtual:physical', 'virt.full_info', expr_form='grain'): if not info: continue if not isinstance(info, dict): continue chunk = {} id_ = next(six.iterkeys(info)) if hyper: if hyper != id_: continue if not isinstance(info[id_], dict): continue if 'ret' not in info[id_]: continue if not isinstance(info[id_]['ret'], dict): continue chunk[id_] = info[id_]['ret'] ret.update(chunk) if not quiet: __jid_event__.fire_event({ 'data': chunk, 'outputter': 'virt_query' }, 'progress') return ret
def absent(name, acl_type, acl_name='', perms='', recurse=False): ''' Ensure a Linux ACL does not exist ''' ret = {'name': name, 'result': True, 'changes': {}, 'comment': ''} _current_perms = __salt__['acl.getfacl'](name) if _current_perms[name].get(acl_type, None): try: user = [i for i in _current_perms[name][acl_type] if next(six.iterkeys(i)) == acl_name].pop() except IndexError: user = None if user: ret['comment'] = 'Removing permissions' if __opts__['test']: ret['result'] = None return ret if recurse: __salt__['acl.delfacl'](acl_type, acl_name, perms, name, recursive=True) else: __salt__['acl.delfacl'](acl_type, acl_name, perms, name) else: ret['comment'] = 'Permissions are in the desired state' else: ret['comment'] = 'ACL Type does not exist' ret['result'] = False return ret
def output(data, **kwargs): # pylint: disable=unused-argument ''' Display output for the salt-run virt.query function ''' out = '' for id_ in data['data']: out += '{0}\n'.format(id_) for vm_ in data['data'][id_]['vm_info']: out += ' {0}\n'.format(vm_) vm_data = data[id_]['vm_info'][vm_] if 'cpu' in vm_data: out += ' CPU: {0}\n'.format(vm_data['cpu']) if 'mem' in vm_data: out += ' Memory: {0}\n'.format(vm_data['mem']) if 'state' in vm_data: out += ' State: {0}\n'.format(vm_data['state']) if 'graphics' in vm_data: if vm_data['graphics'].get('type', '') == 'vnc': out += ' Graphics: vnc - {0}:{1}\n'.format( id_, vm_data['graphics']['port']) if 'disks' in vm_data: for disk, d_data in six.iteritems(vm_data['disks']): out += ' Disk - {0}:\n'.format(disk) out += ' Size: {0}\n'.format(d_data['disk size']) out += ' File: {0}\n'.format(d_data['file']) out += ' File Format: {0}\n'.format( d_data['file format']) if 'nics' in vm_data: for mac in vm_data['nics']: out += ' Nic - {0}:\n'.format(mac) out += ' Source: {0}\n'.format( vm_data['nics'][mac]['source'][next( six.iterkeys(vm_data['nics'][mac]['source']))]) out += ' Type: {0}\n'.format( vm_data['nics'][mac]['type']) return out
def service_highstate(requires=True): """ Return running and enabled services in a highstate structure. By default also returns package dependencies for those services, which means that package definitions must be created outside this function. To drop the package dependencies, set ``requires`` to False. CLI Example: salt myminion introspect.service_highstate salt myminion introspect.service_highstate requires=False """ ret = {} running = running_service_owners() for service in running: ret[service] = {"service": ["running"]} if requires: ret[service]["service"].append({"require": {"pkg": running[service]}}) enabled = enabled_service_owners() for service in enabled: if service in ret: ret[service]["service"].append({"enabled": True}) else: ret[service] = {"service": [{"enabled": True}]} if requires: exists = False for item in ret[service]["service"]: if isinstance(item, dict) and next(six.iterkeys(item)) == "require": exists = True if not exists: ret[service]["service"].append({"require": {"pkg": enabled[service]}}) return ret
def send_signal_to_processes(self, signal_): if (salt.utils.platform.is_windows() and signal_ in (signal.SIGTERM, signal.SIGINT)): # On Windows, the subprocesses automatically have their signal # handlers invoked. If you send one of these signals while the # signal handler is running, it will kill the process where it # is currently running and the signal handler will not finish. # This will also break the process tree: children of killed # children will become parentless and not findable when trying # to kill the process tree (they don't inherit their parent's # parent). Hence the 'MWorker' processes would be left over if # the 'ReqServer' process is killed this way since 'taskkill' # with the tree option will not be able to find them. return for pid in six.iterkeys(self._process_map.copy()): try: os.kill(pid, signal_) except OSError as exc: if exc.errno not in (errno.ESRCH, errno.EACCES): # If it's not a "No such process" error, raise it raise # Otherwise, it's a dead process, remove it from the process map del self._process_map[pid]
def start(name): """ Start a named virtual machine """ ret = {} client = salt.client.get_local_client(__opts__["conf_file"]) data = vm_info(name, quiet=True) if not data: __jid_event__.fire_event( {"message": "Failed to find VM {0} to start".format(name)}, "progress" ) return "fail" host = next(six.iterkeys(data)) if data[host][name]["state"] == "running": print("VM {0} is already running".format(name)) return "bad state" try: cmd_ret = client.cmd_iter(host, "virt.start", [name], timeout=600) except SaltClientError as client_error: return "Virtual machine {0} not started: {1}".format(name, client_error) for comp in cmd_ret: ret.update(comp) __jid_event__.fire_event({"message": "Started VM {0}".format(name)}, "progress") return "good"
def __gather_minions(self): ''' Return a list of minions to use for the batch run ''' args = [self.opts['tgt'], 'test.ping', [], self.opts['timeout'], ] selected_target_option = self.opts.get('selected_target_option', None) if selected_target_option is not None: args.append(selected_target_option) else: args.append(self.opts.get('expr_form', 'glob')) ping_gen = self.local.cmd_iter(*args, **self.eauth) fret = set() for ret in ping_gen: m = next(six.iterkeys(ret)) if m is not None: fret.add(m) return (list(fret), ping_gen)
def pause(name): ''' Pause the named vm ''' ret = {} client = salt.client.get_local_client(__opts__['conf_file']) data = vm_info(name, quiet=True) if not data: __jid_event__.fire_event( {'error': 'Failed to find VM {0} to pause'.format(name)}, 'progress') return 'fail' hyper = next(six.iterkeys(data)) if data[hyper][name]['state'] == 'paused': __jid_event__.fire_event( {'error': 'VM {0} is already paused'.format(name)}, 'progress') return 'bad state' cmd_ret = client.cmd_iter(hyper, 'virt.pause', [name], timeout=600) for comp in cmd_ret: ret.update(comp) __jid_event__.fire_event({'message': 'Paused VM {0}'.format(name)}, 'progress') return 'good'
def output(data, **kwargs): # pylint: disable=unused-argument """ Display output for the salt-run virt.query function """ out = "" for id_ in data["data"]: out += "{0}\n".format(id_) for vm_ in data["data"][id_]["vm_info"]: out += " {0}\n".format(vm_) vm_data = data[id_]["vm_info"][vm_] if "cpu" in vm_data: out += " CPU: {0}\n".format(vm_data["cpu"]) if "mem" in vm_data: out += " Memory: {0}\n".format(vm_data["mem"]) if "state" in vm_data: out += " State: {0}\n".format(vm_data["state"]) if "graphics" in vm_data: if vm_data["graphics"].get("type", "") == "vnc": out += " Graphics: vnc - {0}:{1}\n".format( id_, vm_data["graphics"]["port"]) if "disks" in vm_data: for disk, d_data in six.iteritems(vm_data["disks"]): out += " Disk - {0}:\n".format(disk) out += " Size: {0}\n".format(d_data["disk size"]) out += " File: {0}\n".format(d_data["file"]) out += " File Format: {0}\n".format( d_data["file format"]) if "nics" in vm_data: for mac in vm_data["nics"]: out += " Nic - {0}:\n".format(mac) out += " Source: {0}\n".format( vm_data["nics"][mac]["source"][next( six.iterkeys(vm_data["nics"][mac]["source"]))]) out += " Type: {0}\n".format( vm_data["nics"][mac]["type"]) return out
def force_off(name): ''' Force power down the named virtual machine ''' ret = {} client = salt.client.get_local_client(__opts__['conf_file']) data = vm_info(name, quiet=True) if not data: print('Failed to find VM {0} to destroy'.format(name)) return 'fail' host = next(six.iterkeys(data)) if data[host][name]['state'] == 'shutdown': print('VM {0} is already shutdown'.format(name)) return 'bad state' try: cmd_ret = client.cmd_iter(host, 'virt.destroy', [name], timeout=600) except SaltClientError as client_error: return 'Virtual machine {0} could not be forced off: {1}'.format( name, client_error) for comp in cmd_ret: ret.update(comp) __jid_event__.fire_event({'message': 'Powered off VM {0}'.format(name)}, 'progress') return 'good'
def force_off(name): """ Force power down the named virtual machine """ ret = {} client = salt.client.get_local_client(__opts__["conf_file"]) data = vm_info(name, quiet=True) if not data: print("Failed to find VM {0} to destroy".format(name)) return "fail" host = next(six.iterkeys(data)) if data[host][name]["state"] == "shutdown": print("VM {0} is already shutdown".format(name)) return "bad state" try: cmd_ret = client.cmd_iter(host, "virt.stop", [name], timeout=600) except SaltClientError as client_error: return "Virtual machine {0} could not be forced off: {1}".format( name, client_error) for comp in cmd_ret: ret.update(comp) __jid_event__.fire_event({"message": "Powered off VM {0}".format(name)}, "progress") return "good"
def ext_pillar(minion_id, pillar, pillar_name=None): """ A salt external pillar which provides the list of nodegroups of which the minion is a member. :param minion_id: used for compound matching nodegroups :param pillar: provided by salt, but not used by nodegroups ext_pillar :param pillar_name: optional name to use for the pillar, defaults to 'nodegroups' :return: a dictionary which is included by the salt master in the pillars returned to the minion """ pillar_name = pillar_name or "nodegroups" all_nodegroups = __opts__["nodegroups"] nodegroups_minion_is_in = [] ckminions = None for nodegroup_name in six.iterkeys(all_nodegroups): ckminions = ckminions or CkMinions(__opts__) _res = ckminions.check_minions(all_nodegroups[nodegroup_name], "compound") match = _res["minions"] if minion_id in match: nodegroups_minion_is_in.append(nodegroup_name) return {pillar_name: nodegroups_minion_is_in}
def _action(action='get', search=None, one=True, force=False): ''' Multi action helper for start, stop, get, ... ''' vms = {} matched_vms = [] client = salt.client.get_local_client(__opts__['conf_file']) ## lookup vms try: vmadm_args = {} vmadm_args['order'] = 'uuid,alias,hostname,state' if '=' in search: vmadm_args['search'] = search for cn in client.cmd_iter('G@virtual:physical and G@os:smartos', 'vmadm.list', kwarg=vmadm_args, tgt_type='compound'): if not cn: continue node = next(six.iterkeys(cn)) if not isinstance(cn[node], dict) or \ 'ret' not in cn[node] or \ not isinstance(cn[node]['ret'], dict): continue for vm in cn[node]['ret']: vmcfg = cn[node]['ret'][vm] vmcfg['node'] = node vms[vm] = vmcfg except SaltClientError as client_error: pass ## check if we have vms if not vms: return {'Error': 'No vms found.'} ## simple search if '=' not in search: loop_pass = 0 while loop_pass < 3: ## each pass will try a different field if loop_pass == 0: field = 'uuid' elif loop_pass == 1: field = 'hostname' else: field = 'alias' ## loop vms and try to match for vm in vms: if field == 'uuid' and vm == search: matched_vms.append(vm) break # exit for on uuid match (max = 1) elif field in vms[vm] and vms[vm][field] == search: matched_vms.append(vm) ## exit on match(es) or try again if matched_vms: break else: loop_pass += 1 else: for vm in vms: matched_vms.append(vm) ## check if we have vms if not matched_vms: return {'Error': 'No vms matched.'} ## multiple allowed? if one and len(matched_vms) > 1: return { 'Error': 'Matched {0} vms, only one allowed!'.format(len(matched_vms)), 'Matches': matched_vms } ## perform action ret = {} if action in ['start', 'stop', 'reboot', 'get']: for vm in matched_vms: vmadm_args = {'key': 'uuid', 'vm': vm} try: for vmadm_res in client.cmd_iter(vms[vm]['node'], 'vmadm.{0}'.format(action), kwarg=vmadm_args): if not vmadm_res: continue if vms[vm]['node'] in vmadm_res: ret[vm] = vmadm_res[vms[vm]['node']]['ret'] except SaltClientError as client_error: ret[vm] = False elif action in ['is_running']: ret = True for vm in matched_vms: if vms[vm]['state'] != 'running': ret = False break return ret
def run(self, jid=None): ''' Execute the overall routine, print results via outputters ''' fstr = '{0}.prep_jid'.format(self.opts['master_job_cache']) jid = self.returners[fstr]( passed_jid=jid or self.opts.get('jid', None)) # Save the invocation information argv = self.opts['argv'] if self.opts.get('raw_shell', False): fun = 'ssh._raw' args = argv else: fun = argv[0] if argv else '' args = argv[1:] job_load = { 'jid': jid, 'tgt_type': self.tgt_type, 'tgt': self.opts['tgt'], 'user': self.opts['user'], 'fun': fun, 'arg': args, } # save load to the master job cache try: if isinstance(jid, bytes): jid = jid.decode('utf-8') self.returners['{0}.save_load'.format( self.opts['master_job_cache'])](jid, job_load) except Exception as exc: log.exception(exc) log.error('Could not save load with returner {0}: {1}'.format( self.opts['master_job_cache'], exc)) if self.opts.get('verbose'): msg = 'Executing job with jid {0}'.format(jid) print(msg) print('-' * len(msg) + '\n') print('') sret = {} outputter = self.opts.get('output', 'nested') final_exit = 0 for ret in self.handle_ssh(): host = next(six.iterkeys(ret)) if isinstance(ret[host], dict): host_ret = ret[host].get('retcode', 0) if host_ret != 0: final_exit = 1 else: # Error on host final_exit = 1 self.cache_job(jid, host, ret[host], fun) ret = self.key_deploy(host, ret) if not isinstance(ret[host], dict): p_data = {host: ret[host]} elif 'return' not in ret[host]: p_data = ret else: outputter = ret[host].get('out', self.opts.get('output', 'nested')) p_data = {host: ret[host].get('return', {})} if self.opts.get('static'): sret.update(p_data) else: salt.output.display_output(p_data, outputter, self.opts) if self.event: self.event.fire_event( ret, salt.utils.event.tagify([jid, 'ret', host], 'job')) if self.opts.get('static'): salt.output.display_output(sret, outputter, self.opts) if final_exit: sys.exit(salt.defaults.exitcodes.EX_AGGREGATE)
def acl(username): ''' :param username: Username to filter for :return: Dictionary that can be slotted into the ``__opts__`` structure for eauth that designates the user associated ACL Database records such as: =========== ==================== ========= username minion_or_fn_matcher minion_fn =========== ==================== ========= fred test.ping fred server1 network.interfaces fred server1 raid.list fred server2 .* guru .* smartadmin server1 .* =========== ==================== ========= Should result in an eauth config such as: .. code-block:: yaml fred: - test.ping - server1: - network.interfaces - raid.list - server2: - .* guru: - .* smartadmin: - server1: - .* ''' __django_auth_setup() if username is None: db_records = DJANGO_AUTH_CLASS.objects.all() else: db_records = DJANGO_AUTH_CLASS.objects.filter( user_fk__username=username) auth_dict = {} for a in db_records: if a.user_fk.username not in auth_dict: auth_dict[a.user_fk.username] = [] if not a.minion_or_fn_matcher and a.minion_fn: auth_dict[a.user_fk.username].append(a.minion_fn) elif a.minion_or_fn_matcher and not a.minion_fn: auth_dict[a.user_fk.username].append(a.minion_or_fn_matcher) else: found = False for d in auth_dict[a.user_fk.username]: if isinstance(d, dict): if a.minion_or_fn_matcher in six.iterkeys(d): auth_dict[a.user_fk.username][ a.minion_or_fn_matcher].append(a.minion_fn) found = True if not found: auth_dict[a.user_fk.username].append( {a.minion_or_fn_matcher: [a.minion_fn]}) log.debug('django auth_dict is %s', auth_dict) return auth_dict
def list_vms(search=None, verbose=False): ''' List all vms search : string filter vms, see the execution module verbose : boolean print additional information about the vm CLI Example: .. code-block:: bash salt-run vmadm.list salt-run vmadm.list search='type=KVM' salt-run vmadm.list verbose=True ''' ret = OrderedDict() if verbose else [] client = salt.client.get_local_client(__opts__['conf_file']) try: vmadm_args = {} vmadm_args[ 'order'] = 'uuid,alias,hostname,state,type,cpu_cap,vcpus,ram' if search: vmadm_args['search'] = search for cn in client.cmd_iter('G@virtual:physical and G@os:smartos', 'vmadm.list', kwarg=vmadm_args, tgt_type='compound'): if not cn: continue node = next(six.iterkeys(cn)) if not isinstance(cn[node], dict) or \ 'ret' not in cn[node] or \ not isinstance(cn[node]['ret'], dict): continue for vm in cn[node]['ret']: vmcfg = cn[node]['ret'][vm] if verbose: ret[vm] = OrderedDict() ret[vm]['hostname'] = vmcfg['hostname'] ret[vm]['alias'] = vmcfg['alias'] ret[vm]['computenode'] = node ret[vm]['state'] = vmcfg['state'] ret[vm]['resources'] = OrderedDict() ret[vm]['resources']['memory'] = vmcfg['ram'] if vmcfg['type'] == 'KVM': ret[vm]['resources']['cpu'] = "{0:.2f}".format( int(vmcfg['vcpus'])) else: if vmcfg['cpu_cap'] != '': ret[vm]['resources']['cpu'] = "{0:.2f}".format( int(vmcfg['cpu_cap']) / 100) else: ret.append(vm) except SaltClientError as client_error: return "{0}".format(client_error) if not verbose: ret = sorted(ret) return ret
def init( name, cpu, mem, image=None, nic='default', hypervisor=VIRT_DEFAULT_HYPER, start=True, # pylint: disable=redefined-outer-name disk='default', saltenv='base', **kwargs): ''' Initialize a new vm CLI Example: .. code-block:: bash salt 'hypervisor' virt.init vm_name 4 512 salt://path/to/image.raw salt 'hypervisor' virt.init vm_name 4 512 nic=profile disk=profile ''' hypervisor = __salt__['config.get']('libvirt:hypervisor', hypervisor) nicp = _nic_profile(nic, hypervisor, **kwargs) diskp = None seedable = False if image: # with disk template image # if image was used, assume only one disk, i.e. the # 'default' disk profile # TODO: make it possible to use disk profiles and use the # template image as the system disk diskp = _disk_profile('default', hypervisor, **kwargs) # When using a disk profile extract the sole dict key of the first # array element as the filename for disk disk_name = next(six.iterkeys(diskp[0])) disk_type = diskp[0][disk_name]['format'] disk_file_name = '{0}.{1}'.format(disk_name, disk_type) if hypervisor in ['esxi', 'vmware']: # TODO: we should be copying the image file onto the ESX host raise SaltInvocationError('virt.init does not support image ' 'template template in conjunction ' 'with esxi hypervisor') elif hypervisor in ['qemu', 'kvm']: img_dir = __salt__['config.option']('virt.images') img_dest = os.path.join(img_dir, name, disk_file_name) img_dir = os.path.dirname(img_dest) sfn = __salt__['cp.cache_file'](image, saltenv) if not os.path.isdir(img_dir): os.makedirs(img_dir) try: salt.utils.files.copyfile(sfn, img_dest) mask = os.umask(0) os.umask(mask) # Apply umask and remove exec bit mode = (0o0777 ^ mask) & 0o0666 os.chmod(img_dest, mode) except (IOError, OSError): return False seedable = True else: log.error('unsupported hypervisor when handling disk image') else: # no disk template image specified, create disks based on disk profile diskp = _disk_profile(disk, hypervisor, **kwargs) if hypervisor in ['qemu', 'kvm']: # TODO: we should be creating disks in the local filesystem with # qemu-img raise SaltInvocationError('virt.init does not support disk ' 'profiles in conjunction with ' 'qemu/kvm at this time, use image ' 'template instead') else: # assume libvirt manages disks for us for disk in diskp: for disk_name, args in six.iteritems(disk): xml = _gen_vol_xml(name, disk_name, args['size'], hypervisor) define_vol_xml_str(xml) xml = _gen_xml(name, cpu, mem, diskp, nicp, hypervisor, **kwargs) define_xml_str(xml) if kwargs.get('seed') and seedable: install = kwargs.get('install', True) seed_cmd = kwargs.get('seed_cmd', 'seed.apply') __salt__[seed_cmd](img_dest, id_=name, config=kwargs.get('config'), install=install) if start: create(name) return True
def object_present( name, source=None, hash_type=None, extra_args=None, extra_args_from_pillar='boto_s3_object_extra_args', region=None, key=None, keyid=None, profile=None, ): ''' Ensure object exists in S3. name The name of the state definition. This will be used to determine the location of the object in S3, by splitting on the first slash and using the first part as the bucket name and the remainder as the S3 key. source The source file to upload to S3, currently this only supports files hosted on the minion's local file system (starting with /). hash_type Hash algorithm to use to check that the object contents are correct. Defaults to the value of the `hash_type` config option. extra_args A dictionary of extra arguments to use when uploading the file. Note that these are only enforced if new objects are uploaded, and not modified on existing objects. The supported args are those in the ALLOWED_UPLOAD_ARGS list at http://boto3.readthedocs.io/en/latest/reference/customizations/s3.html. However, Note that the 'ACL', 'GrantFullControl', 'GrantRead', 'GrantReadACP', and 'GrantWriteACL' keys are currently not supported. extra_args_from_pillar Name of pillar dict that contains extra arguments. Extra arguments defined for this specific state will be merged over those from the pillar. region Region to connect to. key Secret key to be used. keyid Access key to be used. profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. ''' ret = { 'name': name, 'comment': '', 'changes': {}, } if extra_args is None: extra_args = {} combined_extra_args = copy.deepcopy(__salt__['config.get']( extra_args_from_pillar, {})) __utils__['dictupdate.update'](combined_extra_args, extra_args) if combined_extra_args: supported_args = STORED_EXTRA_ARGS | UPLOAD_ONLY_EXTRA_ARGS combined_extra_args_keys = frozenset(six.iterkeys(combined_extra_args)) extra_keys = combined_extra_args_keys - supported_args if extra_keys: msg = 'extra_args keys {0} are not supported'.format(extra_keys) return {'error': msg} # Get the hash of the local file if not hash_type: hash_type = __opts__['hash_type'] try: digest = salt.utils.hashutils.get_hash(source, form=hash_type) except IOError as e: ret['result'] = False ret['comment'] = "Could not read local file {0}: {1}".format( source, e, ) return ret except ValueError as e: # Invalid hash type exception from get_hash ret['result'] = False ret['comment'] = 'Could not hash local file {0}: {1}'.format( source, e, ) return ret HASH_METADATA_KEY = 'salt_managed_content_hash' combined_extra_args.setdefault('Metadata', {}) if HASH_METADATA_KEY in combined_extra_args['Metadata']: # Be lenient, silently allow hash metadata key if digest value matches if combined_extra_args['Metadata'][HASH_METADATA_KEY] != digest: ret['result'] = False ret['comment'] = ( 'Salt uses the {0} metadata key internally,' 'do not pass it to the boto_s3.object_present state.' ).format(HASH_METADATA_KEY) return ret combined_extra_args['Metadata'][HASH_METADATA_KEY] = digest # Remove upload-only keys from full set of extra_args # to create desired dict for comparisons desired_metadata = dict((k, v) for k, v in six.iteritems(combined_extra_args) if k not in UPLOAD_ONLY_EXTRA_ARGS) # Some args (SSE-C, RequestPayer) must also be passed to get_metadata metadata_extra_args = dict((k, v) for k, v in six.iteritems(combined_extra_args) if k in GET_METADATA_EXTRA_ARGS) r = __salt__['boto_s3.get_object_metadata']( name, extra_args=metadata_extra_args, region=region, key=key, keyid=keyid, profile=profile, ) if 'error' in r: ret['result'] = False ret['comment'] = 'Failed to check if S3 object exists: {0}.'.format( r['error'], ) return ret if r['result']: # Check if content and metadata match # A hash of the content is injected into the metadata, # so we can combine both checks into one # Only check metadata keys specified by the user, # ignore other fields that have been set s3_metadata = dict((k, r['result'][k]) for k in STORED_EXTRA_ARGS if k in desired_metadata and k in r['result']) if s3_metadata == desired_metadata: ret['result'] = True ret['comment'] = 'S3 object {0} is present.'.format(name) return ret action = 'update' else: s3_metadata = None action = 'create' def _yaml_safe_dump(attrs): ''' Safely dump YAML using a readable flow style ''' dumper_name = 'IndentedSafeOrderedDumper' dumper = __utils__['yaml.get_dumper'](dumper_name) return __utils__['yaml.dump'](attrs, default_flow_style=False, Dumper=dumper) changes_diff = ''.join( difflib.unified_diff( _yaml_safe_dump(s3_metadata).splitlines(True), _yaml_safe_dump(desired_metadata).splitlines(True), )) if __opts__['test']: ret['result'] = None ret['comment'] = 'S3 object {0} set to be {1}d.'.format(name, action) ret['comment'] += '\nChanges:\n{0}'.format(changes_diff) ret['pchanges'] = {'diff': changes_diff} return ret r = __salt__['boto_s3.upload_file']( source, name, extra_args=combined_extra_args, region=region, key=key, keyid=keyid, profile=profile, ) if 'error' in r: ret['result'] = False ret['comment'] = 'Failed to {0} S3 object: {1}.'.format( action, r['error'], ) return ret ret['result'] = True ret['comment'] = 'S3 object {0} {1}d.'.format(name, action) ret['comment'] += '\nChanges:\n{0}'.format(changes_diff) ret['changes'] = {'diff': changes_diff} return ret
def _low(self, fun, low): ''' Execute a function from low data Low data includes: required: - fun: the name of the function to run optional: - args: a list of args to pass to fun - kwargs: kwargs for fun - __user__: user who is running the command - __jid__: jid to run under - __tag__: tag to run under ''' # fire the mminion loading (if not already done) here # this is not to clutter the output with the module loading # if we have a high debug level. self.mminion # pylint: disable=W0104 jid = low.get('__jid__', salt.utils.jid.gen_jid()) tag = low.get('__tag__', tagify(jid, prefix=self.tag_prefix)) data = { 'fun': '{0}.{1}'.format(self.client, fun), 'jid': jid, 'user': low.get('__user__', 'UNKNOWN'), } event = salt.utils.event.get_event('master', self.opts['sock_dir'], self.opts['transport'], opts=self.opts, listen=False) namespaced_event = salt.utils.event.NamespacedEvent( event, tag, print_func=self.print_async_event if hasattr( self, 'print_async_event') else None) # TODO: document these, and test that they exist # TODO: Other things to inject?? func_globals = { '__jid__': jid, '__user__': data['user'], '__tag__': tag, # weak ref to avoid the Exception in interpreter # teardown of event '__jid_event__': weakref.proxy(namespaced_event), } func_globals['__jid_event__'].fire_event(data, 'new') try: verify_fun(self.functions, fun) # Inject some useful globals to *all* the function's global # namespace only once per module-- not per func completed_funcs = [] for mod_name in six.iterkeys(self.functions): if '.' not in mod_name: continue mod, _ = mod_name.split('.', 1) if mod in completed_funcs: continue completed_funcs.append(mod) for global_key, value in six.iteritems(func_globals): self.functions[mod_name].__globals__[global_key] = value # There are some descrepencies of what a "low" structure is in the # publisher world it is a dict including stuff such as jid, fun, # arg (a list of args, with kwargs packed in). Historically this # particular one has had no "arg" and just has had all the kwargs # packed into the top level object. The plan is to move away from # that since the caller knows what is an arg vs a kwarg, but while # we make the transition we will load "kwargs" using format_call if # there are no kwargs in the low object passed in f_call = None if 'arg' not in low: f_call = salt.utils.format_call( self.functions[fun], low, expected_extra_kws=CLIENT_INTERNAL_KEYWORDS) args = f_call.get('arg', ()) else: args = low['arg'] if 'kwarg' not in low: if f_call is None: f_call = salt.utils.format_call( self.functions[fun], low, expected_extra_kws=CLIENT_INTERNAL_KEYWORDS) kwargs = f_call.get('kwarg', {}) # throw a warning for the badly formed low data if we found # kwargs using the old mechanism if kwargs: salt.utils.warn_until( 'Carbon', 'kwargs must be passed inside the low under "kwargs"') else: kwargs = low['kwarg'] # Initialize a context for executing the method. with tornado.stack_context.StackContext( self.functions.context_dict.clone): data['return'] = self.functions[fun](*args, **kwargs) data['success'] = True except (Exception, SystemExit) as ex: if isinstance(ex, salt.exceptions.NotImplemented): data['return'] = str(ex) else: data['return'] = 'Exception occurred in {0} {1}: {2}'.format( self.client, fun, traceback.format_exc(), ) data['success'] = False namespaced_event.fire_event(data, 'ret') try: salt.utils.job.store_job( self.opts, { 'id': self.opts['id'], 'tgt': self.opts['id'], 'jid': data['jid'], 'return': data, }, event=None, mminion=self.mminion, ) except salt.exceptions.SaltCacheError: log.error( 'Could not store job cache info. Job details for this run may be unavailable.' ) # if we fired an event, make sure to delete the event object. # This will ensure that we call destroy, which will do the 0MQ linger log.info('Runner completed: {0}'.format(data['jid'])) del event del namespaced_event return data['return']
def _call_function(name, returner=None, **kwargs): ''' Calls a function from the specified module. :param name: :param kwargs: :return: ''' argspec = salt.utils.args.get_function_argspec(__salt__[name]) # func_kw is initialized to a dictionary of keyword arguments the function to be run accepts func_kw = dict( zip( argspec.args[-len(argspec.defaults or []):], # pylint: disable=incompatible-py3-code argspec.defaults or [])) # func_args is initialized to a list of positional arguments that the function to be run accepts func_args = argspec.args[:len(argspec.args or []) - len(argspec.defaults or [])] arg_type, na_type, kw_type = [], {}, False for funcset in reversed(kwargs.get('func_args') or []): if not isinstance(funcset, dict): # We are just receiving a list of args to the function to be run, so just append # those to the arg list that we will pass to the func. arg_type.append(funcset) else: for kwarg_key in six.iterkeys(funcset): # We are going to pass in a keyword argument. The trick here is to make certain # that if we find that in the *args* list that we pass it there and not as a kwarg if kwarg_key in func_args: arg_type.append(funcset[kwarg_key]) continue else: # Otherwise, we're good and just go ahead and pass the keyword/value pair into # the kwargs list to be run. func_kw.update(funcset) arg_type.reverse() _exp_prm = len(argspec.args or []) - len(argspec.defaults or []) _passed_prm = len(arg_type) missing = [] if na_type and _exp_prm > _passed_prm: for arg in argspec.args: if arg not in func_kw: missing.append(arg) if missing: raise SaltInvocationError('Missing arguments: {0}'.format( ', '.join(missing))) elif _exp_prm > _passed_prm: raise SaltInvocationError( 'Function expects {0} parameters, got only {1}'.format( _exp_prm, _passed_prm)) mret = __salt__[name](*arg_type, **func_kw) if returner is not None: returners = salt.loader.returners(__opts__, __salt__) if returner in returners: returners[returner]({ 'id': __opts__['id'], 'ret': mret, 'fun': name, 'jid': salt.utils.jid.gen_jid(__opts__) }) return mret
def low(self, fun, low, print_event=True, full_return=False): ''' Execute a function from low data Low data includes: required: - fun: the name of the function to run optional: - arg: a list of args to pass to fun - kwarg: kwargs for fun - __user__: user who is running the command - __jid__: jid to run under - __tag__: tag to run under ''' # fire the mminion loading (if not already done) here # this is not to clutter the output with the module loading # if we have a high debug level. self.mminion # pylint: disable=W0104 jid = low.get('__jid__', salt.utils.jid.gen_jid(self.opts)) tag = low.get('__tag__', salt.utils.event.tagify(jid, prefix=self.tag_prefix)) data = { 'fun': '{0}.{1}'.format(self.client, fun), 'jid': jid, 'user': low.get('__user__', 'UNKNOWN'), } event = salt.utils.event.get_event('master', self.opts['sock_dir'], self.opts['transport'], opts=self.opts, listen=False) if print_event: print_func = self.print_async_event \ if hasattr(self, 'print_async_event') \ else None else: # Suppress printing of return event (this keeps us from printing # runner/wheel output during orchestration). print_func = None namespaced_event = salt.utils.event.NamespacedEvent( event, tag, print_func=print_func) # TODO: test that they exist # TODO: Other things to inject?? func_globals = { '__jid__': jid, '__user__': data['user'], '__tag__': tag, # weak ref to avoid the Exception in interpreter # teardown of event '__jid_event__': weakref.proxy(namespaced_event), } try: self_functions = pycopy.copy(self.functions) salt.utils.lazy.verify_fun(self_functions, fun) # Inject some useful globals to *all* the function's global # namespace only once per module-- not per func completed_funcs = [] for mod_name in six.iterkeys(self_functions): if '.' not in mod_name: continue mod, _ = mod_name.split('.', 1) if mod in completed_funcs: continue completed_funcs.append(mod) for global_key, value in six.iteritems(func_globals): self.functions[mod_name].__globals__[global_key] = value # There are some discrepancies of what a "low" structure is in the # publisher world it is a dict including stuff such as jid, fun, # arg (a list of args, with kwargs packed in). Historically this # particular one has had no "arg" and just has had all the kwargs # packed into the top level object. The plan is to move away from # that since the caller knows what is an arg vs a kwarg, but while # we make the transition we will load "kwargs" using format_call if # there are no kwargs in the low object passed in. if 'arg' in low and 'kwarg' in low: args = low['arg'] kwargs = low['kwarg'] else: f_call = salt.utils.args.format_call( self.functions[fun], low, expected_extra_kws=CLIENT_INTERNAL_KEYWORDS) args = f_call.get('args', ()) kwargs = f_call.get('kwargs', {}) # Update the event data with loaded args and kwargs data['fun_args'] = list(args) + ([kwargs] if kwargs else []) func_globals['__jid_event__'].fire_event(data, 'new') # Initialize a context for executing the method. with tornado.stack_context.StackContext( self.functions.context_dict.clone): func = self.functions[fun] try: data['return'] = func(*args, **kwargs) except TypeError as exc: data['return'] = salt.utils.text.cli_info( 'Error: {exc}\nUsage:\n{doc}'.format(exc=exc, doc=func.__doc__), 'Passed invalid arguments') except Exception as exc: data['return'] = salt.utils.text.cli_info( six.text_type(exc), 'General error occurred') try: data['success'] = self.context.get('retcode', 0) == 0 except AttributeError: # Assume a True result if no context attribute data['success'] = True if isinstance(data['return'], dict) and 'data' in data['return']: # some functions can return boolean values data['success'] = salt.utils.state.check_result( data['return']['data']) except (Exception, SystemExit) as ex: if isinstance(ex, salt.exceptions.NotImplemented): data['return'] = six.text_type(ex) else: data[ 'return'] = 'Exception occurred in {client} {fun}: {tb}'.format( client=self.client, fun=fun, tb=traceback.format_exc()) data['success'] = False if self.store_job: try: salt.utils.job.store_job( self.opts, { 'id': self.opts['id'], 'tgt': self.opts['id'], 'jid': data['jid'], 'return': data, }, event=None, mminion=self.mminion, ) except salt.exceptions.SaltCacheError: log.error('Could not store job cache info. ' 'Job details for this run may be unavailable.') # Outputters _can_ mutate data so write to the job cache first! namespaced_event.fire_event(data, 'ret') # if we fired an event, make sure to delete the event object. # This will ensure that we call destroy, which will do the 0MQ linger log.info('Runner completed: %s', data['jid']) del event del namespaced_event return data if full_return else data['return']
def present(name, acl_type, acl_name='', perms='', recurse=False): ''' Ensure a Linux ACL is present ''' ret = {'name': name, 'result': True, 'changes': {}, 'comment': ''} _octal = {'r': 4, 'w': 2, 'x': 1, '-': 0} __current_perms = __salt__['acl.getfacl'](name) if acl_type.startswith(('d:', 'default:')): _acl_type = ':'.join(acl_type.split(':')[1:]) _current_perms = __current_perms[name].get('defaults', {}) _default = True else: _acl_type = acl_type _current_perms = __current_perms[name] _default = False # The getfacl execution module lists default with empty names as being # applied to the user/group that owns the file, e.g., # default:group::rwx would be listed as default:group:root:rwx # In this case, if acl_name is empty, we really want to search for root # We search through the dictionary getfacl returns for the owner of the # file if acl_name is empty. if acl_name == '': _search_name = __current_perms[name].get('comment').get(_acl_type) else: _search_name = acl_name if _current_perms.get(_acl_type, None) or _default: try: user = [ i for i in _current_perms[_acl_type] if next(six.iterkeys(i)) == _search_name ].pop() except (AttributeError, IndexError, StopIteration, KeyError): user = None if user: if user[_search_name]['octal'] == sum( [_octal.get(i, i) for i in perms]): ret['comment'] = 'Permissions are in the desired state' else: ret['comment'] = 'Permissions have been updated' if __opts__['test']: ret['result'] = None return ret if recurse: __salt__['acl.modfacl'](acl_type, acl_name, perms, name, recursive=True) else: __salt__['acl.modfacl'](acl_type, acl_name, perms, name) else: ret['comment'] = 'Permissions will be applied' if __opts__['test']: ret['result'] = None return ret if recurse: __salt__['acl.modfacl'](acl_type, acl_name, perms, name, recursive=True) else: __salt__['acl.modfacl'](acl_type, acl_name, perms, name) else: ret['comment'] = 'ACL Type does not exist' ret['result'] = False return ret
def create_user(username, password, permissions, users=None): ''' Create user accounts CLI Example: .. code-block:: bash salt dell drac.create_user [USERNAME] [PASSWORD] [PRIVILEGES] salt dell drac.create_user diana secret login,test_alerts,clear_logs DRAC Privileges * login : Login to iDRAC * drac : Configure iDRAC * user_management : Configure Users * clear_logs : Clear Logs * server_control_commands : Execute Server Control Commands * console_redirection : Access Console Redirection * virtual_media : Access Virtual Media * test_alerts : Test Alerts * debug_commands : Execute Debug Commands ''' _uids = set() if users is None: users = list_users() if username in users: log.warning('\'{0}\' already exists'.format(username)) return False for idx in six.iterkeys(users): _uids.add(users[idx]['index']) uid = sorted(list(set(range(2, 12)) - _uids), reverse=True).pop() # Create user accountvfirst if not __execute_cmd('config -g cfgUserAdmin -o \ cfgUserAdminUserName -i {0} {1}'.format(uid, username)): delete_user(username, uid) return False # Configure users permissions if not set_permissions(username, permissions, uid): log.warning('unable to set user permissions') delete_user(username, uid) return False # Configure users password if not change_password(username, password, uid): log.warning('unable to set user password') delete_user(username, uid) return False # Enable users admin if not __execute_cmd('config -g cfgUserAdmin -o \ cfgUserAdminEnable -i {0} 1'.format(uid)): delete_user(username, uid) return False return True