Exemple #1
0
def _parse_function_definition(fn_def, modulename, ret):
    args = []
    match = re.match(r'def\s+(.*?)\((.*)\):$', fn_def)
    if match is None:
        return
    fn_name = match.group(1)
    if fn_name.startswith('_'):
        return
    if fn_name.endswith('_'):
        fn_name = fn_name[0:-1]
    fn_name = fn_name.strip('"')
    fn_name = fn_name.strip("'")
    try:
        raw_args = match.group(2)
        raw_args = re.sub(r'(.*)\(.*\)(.*)', r'\1\2', raw_args)
        raw_args = re.sub(r'(.*)\'.*\'(.*)', r'\1\2', raw_args)
        individual_args = raw_args.split(',')
        for a in individual_args:
            if '*' in a:
                continue
            args.append(a.split('=')[0].strip())
    except AttributeError:
        pass
    key = '{}.{}'.format(modulename, fn_name)
    if key in ret:
        ret[key].extend(args)
    else:
        ret[key] = args
    ret[key] = list(set(ret[key]))
Exemple #2
0
    def parse_args_and_kwargs(self, cmdline):
        """
        cmdline: list

        returns tuple of: args (list), kwargs (dict)
        """
        # Parse args and kwargs
        args = []
        kwargs = {}

        if len(cmdline) > 1:
            for item in cmdline[1:]:
                if "=" in item:
                    (key, value) = item.split("=", 1)
                    kwargs[key] = value
                else:
                    args.append(item)
        return (args, kwargs)
Exemple #3
0
    def __gather_minions(self):
        '''
        Return a list of minions to use for the batch run
        '''
        args = [
            self.opts['tgt'],
            'test.ping',
            [],
            self.opts.get('batch_presence_ping_timeout', 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('tgt_type', 'glob'))

        self.pub_kwargs['yield_pub_data'] = True
        ping_gen = self.local.cmd_iter(
            *args,
            gather_job_timeout=self.opts.get(
                'batch_presence_ping_gather_job_timeout',
                self.opts['gather_job_timeout']),
            **self.pub_kwargs)

        # Broadcast to targets
        fret = set()
        nret = set()
        for ret in ping_gen:
            if ('minions' and 'jid') in ret:
                for minion in ret['minions']:
                    nret.add(minion)
                continue
            else:
                try:
                    m = next(six.iterkeys(ret))
                except StopIteration:
                    if not self.quiet:
                        salt.utils.stringutils.print_cli(
                            'No minions matched the target.')
                    break
                if m is not None:
                    fret.add(m)
        return (list(fret), ping_gen, nret.difference(fret))
Exemple #4
0
    def _get_args_kwargs(self, fun, args=None):
        argspec = salt.utils.args.get_function_argspec(fun)
        if args is None:
            args = []
            if argspec.args:
                # Iterate in reverse order to ensure we get the correct default
                # value for the positional argument.
                for arg, default in zip_longest(
                        reversed(argspec.args), reversed(argspec.defaults
                                                         or ())):
                    args.append(self.opts.get(arg, default))
            # Reverse the args so that they are in the correct order
            args = args[::-1]

        if argspec.keywords is None:
            kwargs = {}
        else:
            args, kwargs = salt.minion.load_args_and_kwargs(fun, args)
        return args, kwargs
Exemple #5
0
    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("tgt_type", "glob"))

        self.pub_kwargs["yield_pub_data"] = True
        ping_gen = self.local.cmd_iter(
            *args,
            gather_job_timeout=self.opts["gather_job_timeout"],
            **self.pub_kwargs)

        # Broadcast to targets
        fret = set()
        nret = set()
        for ret in ping_gen:
            if ("minions" and "jid") in ret:
                for minion in ret["minions"]:
                    nret.add(minion)
                continue
            else:
                try:
                    m = next(six.iterkeys(ret))
                except StopIteration:
                    if not self.quiet:
                        salt.utils.stringutils.print_cli(
                            "No minions matched the target.")
                    break
                if m is not None:
                    fret.add(m)
        return (list(fret), ping_gen, nret.difference(fret))
Exemple #6
0
 def __arg_comps(self):
     '''
     Return the function name and the arg list
     '''
     fun = self.argv[0] if self.argv else ''
     args = []
     kws = {}
     for arg in self.argv[1:]:
         # FIXME - there is a bug here that will steal a non-keyword argument.
         # example:
         #
         # .. code-block:: bash
         #
         #     salt-ssh '*' cmd.run_all 'n=$((RANDOM%8)); exit $n'
         #
         # The 'n=' appears to be a keyword argument, but it is
         # simply the argument!
         if re.match(r'\w+=', arg):
             (key, val) = arg.split('=', 1)
             kws[key] = val
         else:
             args.append(arg)
     return fun, args, kws
Exemple #7
0
 def __arg_comps(self):
     '''
     Return the function name and the arg list
     '''
     fun = self.argv[0] if self.argv else ''
     args = []
     kws = {}
     for arg in self.argv[1:]:
         # FIXME - there is a bug here that will steal a non-keyword argument.
         # example:
         #
         # .. code-block:: bash
         #
         #     salt-ssh '*' cmd.run_all 'n=$((RANDOM%8)); exit $n'
         #
         # The 'n=' appears to be a keyword argument, but it is
         # simply the argument!
         if re.match(r'\w+=', arg):
             (key, val) = arg.split('=', 1)
             kws[key] = val
         else:
             args.append(arg)
     return fun, args, kws
Exemple #8
0
def _run(name, **kwargs):
    '''
    .. deprecated:: 2017.7.0
       Function name stays the same, behaviour will change.

    Run a single module function

    ``name``
        The module function to execute

    ``returner``
        Specify the returner to send the return of the module execution to

    ``kwargs``
        Pass any arguments needed to execute the function
    '''
    ret = {'name': name, 'changes': {}, 'comment': '', 'result': None}
    if name not in __salt__:
        ret['comment'] = 'Module function {0} is not available'.format(name)
        ret['result'] = False
        return ret

    if __opts__['test']:
        ret['comment'] = 'Module function {0} is set to execute'.format(name)
        return ret

    aspec = salt.utils.args.get_function_argspec(__salt__[name])
    args = []
    defaults = {}

    arglen = 0
    deflen = 0
    if isinstance(aspec.args, list):
        arglen = len(aspec.args)
    if isinstance(aspec.defaults, tuple):
        deflen = len(aspec.defaults)
    # Match up the defaults with the respective args
    for ind in range(arglen - 1, -1, -1):
        minus = arglen - ind
        if deflen - minus > -1:
            defaults[aspec.args[ind]] = aspec.defaults[-minus]
    # overwrite passed default kwargs
    for arg in defaults:
        if arg == 'name':
            if 'm_name' in kwargs:
                defaults[arg] = kwargs.pop('m_name')
        elif arg == 'fun':
            if 'm_fun' in kwargs:
                defaults[arg] = kwargs.pop('m_fun')
        elif arg == 'state':
            if 'm_state' in kwargs:
                defaults[arg] = kwargs.pop('m_state')
        elif arg == 'saltenv':
            if 'm_saltenv' in kwargs:
                defaults[arg] = kwargs.pop('m_saltenv')
        if arg in kwargs:
            defaults[arg] = kwargs.pop(arg)
    missing = set()
    for arg in aspec.args:
        if arg == 'name':
            rarg = 'm_name'
        elif arg == 'fun':
            rarg = 'm_fun'
        elif arg == 'names':
            rarg = 'm_names'
        elif arg == 'state':
            rarg = 'm_state'
        elif arg == 'saltenv':
            rarg = 'm_saltenv'
        else:
            rarg = arg
        if rarg not in kwargs and arg not in defaults:
            missing.add(rarg)
            continue
        if arg in defaults:
            args.append(defaults[arg])
        else:
            args.append(kwargs.pop(rarg))
    if missing:
        comment = 'The following arguments are missing:'
        for arg in missing:
            comment += ' {0}'.format(arg)
        ret['comment'] = comment
        ret['result'] = False
        return ret

    if aspec.varargs:
        if aspec.varargs == 'name':
            rarg = 'm_name'
        elif aspec.varargs == 'fun':
            rarg = 'm_fun'
        elif aspec.varargs == 'names':
            rarg = 'm_names'
        elif aspec.varargs == 'state':
            rarg = 'm_state'
        elif aspec.varargs == 'saltenv':
            rarg = 'm_saltenv'
        else:
            rarg = aspec.varargs

        if rarg in kwargs:
            varargs = kwargs.pop(rarg)

            if not isinstance(varargs, list):
                msg = "'{0}' must be a list."
                ret['comment'] = msg.format(aspec.varargs)
                ret['result'] = False
                return ret

            args.extend(varargs)

    nkwargs = {}
    if aspec.keywords and aspec.keywords in kwargs:
        nkwargs = kwargs.pop(aspec.keywords)

        if not isinstance(nkwargs, dict):
            msg = "'{0}' must be a dict."
            ret['comment'] = msg.format(aspec.keywords)
            ret['result'] = False
            return ret

    try:
        if aspec.keywords:
            mret = __salt__[name](*args, **nkwargs)
        else:
            mret = __salt__[name](*args)
    except Exception as e:
        ret['comment'] = 'Module function {0} threw an exception. Exception: {1}'.format(
            name, e)
        ret['result'] = False
        return ret
    else:
        if mret is not None or mret is not {}:
            ret['changes']['ret'] = mret

    if 'returner' in kwargs:
        ret_ret = {
            'id': __opts__['id'],
            'ret': mret,
            'fun': name,
            'jid': salt.utils.jid.gen_jid(__opts__)
        }
        returners = salt.loader.returners(__opts__, __salt__)
        if kwargs['returner'] in returners:
            returners[kwargs['returner']](ret_ret)
    ret['comment'] = 'Module function {0} executed'.format(name)
    ret['result'] = _get_result(mret, ret['changes'])

    return ret
Exemple #9
0
def start(token,
          room='salt',
          aliases=None,
          valid_users=None,
          valid_commands=None,
          control=False,
          trigger="!",
          tag='salt/engines/hipchat/incoming',
          api_key=None,
          api_url=None,
          max_rooms=None,
          wait_time=None,
          output_type='file',
          outputter='nested'):
    '''
    Listen to Hipchat messages and forward them to Salt.

    token
        The HipChat API key. It requires a key for global usgae,
        assigned per user, rather than room.

    room
        The HipChat room name.

    aliases
        Define custom aliases.

    valid_users
        Restrict access only to certain users.

    valid_commands
        Restrict the execution to a limited set of commands.

    control
        Send commands to the master.

    trigger: ``!``
        Special character that triggers the execution of salt commands.

    tag: ``salt/engines/hipchat/incoming``
        The event tag on the Salt bus.

    api_url: ``https://api.hipchat.com``
        The URL to the HipChat API.

        .. versionadded:: 2017.7.0

    max_rooms: ``1000``
        Maximum number of rooms allowed to fetch. If set to 0,
        it is able to retrieve the entire list of rooms.

    wait_time: ``5``
        Maximum wait time, in seconds.

    output_type: ``file``
        The type of the output. Choose bewteen:

            - ``file``: save the output into a temporary file and upload
            - ``html``: send the output as HTML
            - ``code``: send the output as code

        This can be overriden when executing a command, using the ``--out-type`` argument.

        .. versionadded:: 2017.7.0

    outputter: ``nested``
        The format to display the data, using the outputters available on the CLI.
        This argument can also be overriden when executing a command, using the ``--out`` option.

        .. versionadded:: 2017.7.0

    HipChat Example:

    .. code-block:: text

        ! test.ping
        ! test.ping target=minion1
        ! test.ping --out=nested
        ! test.ping --out-type=code --out=table
    '''
    target_room = None

    if __opts__.get('__role') == 'master':
        fire_master = salt.utils.event.get_master_event(
            __opts__,
            __opts__['sock_dir']).fire_event
    else:
        fire_master = None

    def fire(tag, msg):
        '''
        fire event to salt bus
        '''

        if fire_master:
            fire_master(msg, tag)
        else:
            __salt__['event.send'](tag, msg)

    def _eval_bot_mentions(all_messages, trigger):
        ''' yield partner message '''
        for message in all_messages:
            message_text = message['message']
            if message_text.startswith(trigger):
                fire(tag, message)
                text = message_text.replace(trigger, '').strip()
                yield message['from']['mention_name'], text

    token = token or api_key
    if not token:
        raise UserWarning("Hipchat token not found")

    runner_functions = sorted(salt.runner.Runner(__opts__).functions)

    if not api_url:
        api_url = _DEFAULT_API_URL
    hipc = hypchat.HypChat(token, endpoint=api_url)
    if not hipc:
        raise UserWarning("Unable to connect to hipchat")

    log.debug('Connected to Hipchat')
    rooms_kwargs = {}
    if max_rooms is None:
        max_rooms = _DEFAULT_MAX_ROOMS
        rooms_kwargs['max_results'] = max_rooms
    elif max_rooms > 0:
        rooms_kwargs['max_results'] = max_rooms
    # if max_rooms is 0 => retrieve all (rooms_kwargs is empty dict)
    all_rooms = hipc.rooms(**rooms_kwargs)['items']
    for a_room in all_rooms:
        if a_room['name'] == room:
            target_room = a_room
    if not target_room:
        log.debug("Unable to connect to room %s", room)
        # wait for a bit as to not burn through api calls
        time.sleep(30)
        raise UserWarning("Unable to connect to room {0}".format(room))

    after_message_id = target_room.latest(maxResults=1)['items'][0]['id']

    while True:
        try:
            new_messages = target_room.latest(
                not_before=after_message_id)['items']
        except hypchat.requests.HttpServiceUnavailable:
            time.sleep(15)
            continue

        after_message_id = new_messages[-1]['id']
        for partner, text in _eval_bot_mentions(new_messages[1:], trigger):
            # bot summoned by partner

            if not control:
                log.debug("Engine not configured for control")
                return

            # Ensure the user is allowed to run commands
            if valid_users:
                if partner not in valid_users:
                    target_room.message('{0} not authorized to run Salt commands'.format(partner))
                    return

            args = []
            kwargs = {}

            cmdline = salt.utils.args.shlex_split(text)
            cmd = cmdline[0]

            # Evaluate aliases
            if aliases and isinstance(aliases, dict) and cmd in aliases.keys():
                cmdline = aliases[cmd].get('cmd')
                cmdline = salt.utils.args.shlex_split(cmdline)
                cmd = cmdline[0]

            # Parse args and kwargs
            if len(cmdline) > 1:
                for item in cmdline[1:]:
                    if '=' in item:
                        (key, value) = item.split('=', 1)
                        kwargs[key] = value
                    else:
                        args.append(item)

            # Check for target. Otherwise assume *
            if 'target' not in kwargs:
                target = '*'
            else:
                target = kwargs['target']
                del kwargs['target']

            # Check for tgt_type. Otherwise assume glob
            if 'tgt_type' not in kwargs:
                tgt_type = 'glob'
            else:
                tgt_type = kwargs['tgt_type']
                del kwargs['tgt_type']

            # Check for outputter. Otherwise assume nested
            if '--out' in kwargs:
                outputter = kwargs['--out']
                del kwargs['--out']

            # Check for outputter. Otherwise assume nested
            if '--out-type' in kwargs:
                output_type = kwargs['--out-type']
                del kwargs['--out-type']

            # Ensure the command is allowed
            if valid_commands:
                if cmd not in valid_commands:
                    target_room.message('Using {0} is not allowed.'.format(cmd))
                    return

            ret = {}
            if cmd in runner_functions:
                runner = salt.runner.RunnerClient(__opts__)
                ret = runner.cmd(cmd, arg=args, kwarg=kwargs)

            # Default to trying to run as a client module.
            else:
                local = salt.client.LocalClient()
                ret = local.cmd('{0}'.format(target), cmd, args, kwargs, tgt_type='{0}'.format(tgt_type))

            nice_args = (' ' + ' '.join(args)) if args else ''
            nice_kwargs = (' ' + ' '.join('{0}={1}'.format(key, value) for (key, value) in six.iteritems(kwargs))) \
                          if kwargs else ''
            message_string = '@{0} Results for: {1}{2}{3} on {4}'.format(partner,
                                                                         cmd,
                                                                         nice_args,
                                                                         nice_kwargs,
                                                                         target)
            if output_type == 'html':
                _publish_html_message(token, room, ret, message=message_string, outputter=outputter, api_url=api_url)
            elif output_type == 'code':
                _publish_code_message(token, room, ret, message=message_string, outputter=outputter, api_url=api_url)
            else:
                tmp_path_fn = salt.utils.files.mkstemp()
                with salt.utils.files.fopen(tmp_path_fn, 'w+') as fp_:
                    salt.utils.json.dump(ret, fp_, sort_keys=True, indent=4)
                _publish_file(token, room, tmp_path_fn, message=message_string, api_url=api_url)
                salt.utils.files.safe_rm(tmp_path_fn)
        time.sleep(wait_time or _DEFAULT_SLEEP)
Exemple #10
0
def _run(name, **kwargs):
    """
    .. deprecated:: 2017.7.0
       Function name stays the same, behaviour will change.

    Run a single module function

    ``name``
        The module function to execute

    ``returner``
        Specify the returner to send the return of the module execution to

    ``kwargs``
        Pass any arguments needed to execute the function
    """
    ret = {"name": name, "changes": {}, "comment": "", "result": None}
    if name not in __salt__:
        ret["comment"] = "Module function {0} is not available".format(name)
        ret["result"] = False
        return ret

    if __opts__["test"]:
        ret["comment"] = "Module function {0} is set to execute".format(name)
        return ret

    aspec = salt.utils.args.get_function_argspec(__salt__[name])
    args = []
    defaults = {}

    arglen = 0
    deflen = 0
    if isinstance(aspec.args, list):
        arglen = len(aspec.args)
    if isinstance(aspec.defaults, tuple):
        deflen = len(aspec.defaults)
    # Match up the defaults with the respective args
    for ind in range(arglen - 1, -1, -1):
        minus = arglen - ind
        if deflen - minus > -1:
            defaults[aspec.args[ind]] = aspec.defaults[-minus]
    # overwrite passed default kwargs
    for arg in defaults:
        if arg == "name":
            if "m_name" in kwargs:
                defaults[arg] = kwargs.pop("m_name")
        elif arg == "fun":
            if "m_fun" in kwargs:
                defaults[arg] = kwargs.pop("m_fun")
        elif arg == "state":
            if "m_state" in kwargs:
                defaults[arg] = kwargs.pop("m_state")
        elif arg == "saltenv":
            if "m_saltenv" in kwargs:
                defaults[arg] = kwargs.pop("m_saltenv")
        if arg in kwargs:
            defaults[arg] = kwargs.pop(arg)
    missing = set()
    for arg in aspec.args:
        if arg == "name":
            rarg = "m_name"
        elif arg == "fun":
            rarg = "m_fun"
        elif arg == "names":
            rarg = "m_names"
        elif arg == "state":
            rarg = "m_state"
        elif arg == "saltenv":
            rarg = "m_saltenv"
        else:
            rarg = arg
        if rarg not in kwargs and arg not in defaults:
            missing.add(rarg)
            continue
        if arg in defaults:
            args.append(defaults[arg])
        else:
            args.append(kwargs.pop(rarg))
    if missing:
        comment = "The following arguments are missing:"
        for arg in missing:
            comment += " {0}".format(arg)
        ret["comment"] = comment
        ret["result"] = False
        return ret

    if aspec.varargs:
        if aspec.varargs == "name":
            rarg = "m_name"
        elif aspec.varargs == "fun":
            rarg = "m_fun"
        elif aspec.varargs == "names":
            rarg = "m_names"
        elif aspec.varargs == "state":
            rarg = "m_state"
        elif aspec.varargs == "saltenv":
            rarg = "m_saltenv"
        else:
            rarg = aspec.varargs

        if rarg in kwargs:
            varargs = kwargs.pop(rarg)

            if not isinstance(varargs, list):
                msg = "'{0}' must be a list."
                ret["comment"] = msg.format(aspec.varargs)
                ret["result"] = False
                return ret

            args.extend(varargs)

    nkwargs = {}
    if aspec.keywords and aspec.keywords in kwargs:
        nkwargs = kwargs.pop(aspec.keywords)
        if not isinstance(nkwargs, dict):
            msg = "'{0}' must be a dict."
            ret["comment"] = msg.format(aspec.keywords)
            ret["result"] = False
            return ret

    try:
        if aspec.keywords:
            mret = __salt__[name](*args, **nkwargs)
        else:
            mret = __salt__[name](*args)
    except Exception as e:  # pylint: disable=broad-except
        ret["comment"] = "Module function {0} threw an exception. Exception: {1}".format(
            name, e)
        ret["result"] = False
        return ret
    else:
        if mret is not None or mret is not {}:
            ret["changes"]["ret"] = mret

    if "returner" in kwargs:
        ret_ret = {
            "id": __opts__["id"],
            "ret": mret,
            "fun": name,
            "jid": salt.utils.jid.gen_jid(__opts__),
        }
        returners = salt.loader.returners(__opts__, __salt__)
        if kwargs["returner"] in returners:
            returners[kwargs["returner"]](ret_ret)
    ret["comment"] = "Module function {0} executed".format(name)
    ret["result"] = _get_result(mret, ret["changes"])

    return ret
Exemple #11
0
            rarg = "m_name"
        elif arg == "fun":
            rarg = "m_fun"
        elif arg == "names":
            rarg = "m_names"
        elif arg == "state":
            rarg = "m_state"
        elif arg == "saltenv":
            rarg = "m_saltenv"
        else:
            rarg = arg
        if rarg not in kwargs and arg not in defaults:
            missing.add(rarg)
            continue
        if arg in defaults:
            args.append(defaults[arg])
        else:
            args.append(kwargs.pop(rarg))
    if missing:
        comment = "The following arguments are missing:"
        for arg in missing:
            comment += " {0}".format(arg)
        ret["comment"] = comment
        ret["result"] = False
        return ret

    if aspec.varargs:
        if aspec.varargs == "name":
            rarg = "m_name"
        elif aspec.varargs == "fun":
            rarg = "m_fun"