Ejemplo n.º 1
0
    def req_stream(self, path):
        '''
        A thin wrapper to get a response from saltstack api.
        The body of the response will not be downloaded immediately.
        Make sure to close the connection after use.
        api = Pepper('http://ipaddress/api/')
        print(api.login('salt','salt','pam'))
        response = api.req_stream('/events')

        :param path: The path to the salt api resource

        :return: :class:`Response <Response>` object

        :rtype: requests.Response
        '''
        import requests

        headers = {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'X-Requested-With': 'XMLHttpRequest',
        }
        if self.auth and 'token' in self.auth and self.auth['token']:
            headers.setdefault('X-Auth-Token', self.auth['token'])
        else:
            raise PepperException('Authentication required')
            return
        params = {
            'url': self._construct_url(path),
            'headers': headers,
            'verify': self._ssl_verify is True,
            'stream': True
        }
        try:
            resp = requests.get(**params)

            if resp.status_code == 401:
                raise PepperException(
                    str(resp.status_code) + ':Authentication denied')
                return

            if resp.status_code == 500:
                raise PepperException(str(resp.status_code) + ':Server error.')
                return

            if resp.status_code == 404:
                raise PepperException(
                    str(resp.status_code) + ' :This request returns nothing.')
                return
        except PepperException as e:
            print(e)
            return
        return resp
Ejemplo n.º 2
0
    def __init__(self,
                 api_url='https://localhost:8000',
                 debug_http=False,
                 ignore_ssl_errors=False):
        '''
        Initialize the class with the URL of the API

        :param api_url: Host or IP address of the salt-api URL;
            include the port number

        :param debug_http: Add a flag to urllib2 to output the HTTP exchange

        :param ignore_ssl_errors: Add a flag to urllib2 to ignore invalid SSL certificates

        :raises PepperException: if the api_url is misformed

        '''
        split = urlparse.urlsplit(api_url)
        if split.scheme not in ['http', 'https']:
            raise PepperException(
                "salt-api URL missing HTTP(s) protocol: {0}".format(api_url))

        self.api_url = api_url
        self.debug_http = int(debug_http)
        self._ssl_verify = not ignore_ssl_errors
        self.auth = {}
Ejemplo n.º 3
0
    def req_get(self, path):
        '''
        A thin wrapper from get http method of saltstack api
        api = Pepper('http://ipaddress/api/')
        print(api.login('salt','salt','pam'))
        print(api.req_get('/keys'))
        '''
        import requests

        headers = {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'X-Requested-With': 'XMLHttpRequest',
        }
        if self.auth and 'token' in self.auth and self.auth['token']:
            headers.setdefault('X-Auth-Token', self.auth['token'])
        else:
            raise PepperException('Authentication required')
            return
        params = {
            'url': self._construct_url(path),
            'headers': headers,
            'verify': self._ssl_verify is True,
        }
        try:
            resp = requests.get(**params)

            if resp.status_code == 401:
                raise PepperException(
                    str(resp.status_code) + ':Authentication denied')
                return

            if resp.status_code == 500:
                raise PepperException(str(resp.status_code) + ':Server error.')
                return

            if resp.status_code == 404:
                raise PepperException(
                    str(resp.status_code) + ' :This request returns nothing.')
                return
        except PepperException as e:
            print(e)
            return
        return resp.json()
Ejemplo n.º 4
0
    def req_requests(self, path, data=None):
        '''
        A thin wrapper around request and request_kerberos to send
        requests and return the response

        If the current instance contains an authentication token it will be
        attached to the request as a custom header.

        :rtype: dictionary

        '''
        import requests
        from requests_kerberos import HTTPKerberosAuth, OPTIONAL
        auth = HTTPKerberosAuth(mutual_authentication=OPTIONAL)
        headers = {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'X-Requested-With': 'XMLHttpRequest',
        }
        if self.auth and 'token' in self.auth and self.auth['token']:
            headers.setdefault('X-Auth-Token', self.auth['token'])
        # Optionally toggle SSL verification
        params = {
            'url': self._construct_url(path),
            'headers': headers,
            'verify': self._ssl_verify is True,
            'auth': auth,
            'data': json.dumps(data),
        }
        logger.debug('postdata {0}'.format(params))
        resp = requests.post(**params)
        if resp.status_code == 401:
            # TODO should be resp.raise_from_status
            raise PepperException('Authentication denied')
        if resp.status_code == 500:
            # TODO should be resp.raise_from_status
            raise PepperException('Server error.')

        if not self.salt_version and 'x-salt-version' in resp.headers:
            self._parse_salt_version(resp.headers['x-salt-version'])

        return resp.json()
Ejemplo n.º 5
0
    def parse_cmd(self):
        '''
        Extract the low data for a command from the passed CLI params
        '''
        # Short-circuit if JSON was given.
        if self.options.json_input:
            try:
                return json.loads(self.options.json_input)
            except JSONDecodeError:
                raise PepperArgumentsException("Invalid JSON given.")

        if self.options.json_file:
            try:
                with open(self.options.json_file, 'r') as json_content:
                    try:
                        return json.load(json_content)
                    except JSONDecodeError:
                        raise PepperArgumentsException("Invalid JSON given.")
            except FileNotFoundError:
                raise PepperArgumentsException('Cannot open file: %s', self.options.json_file)

        args = list(self.args)

        client = self.options.client if not self.options.batch else 'local_batch'
        low = {'client': client}

        if client.startswith('local'):
            if len(args) < 2:
                self.parser.error("Command or target not specified")

            low['tgt_type'] = self.options.expr_form
            low['tgt'] = args.pop(0)
            low['fun'] = args.pop(0)
            low['batch'] = self.options.batch
            low['arg'] = args
        elif client.startswith('runner'):
            low['fun'] = args.pop(0)
            for arg in args:
                if '=' in arg:
                    key, value = arg.split('=', 1)
                    try:
                        low[key] = json.loads(value)
                    except JSONDecodeError:
                        low[key] = value
                else:
                    low.setdefault('args', []).append(arg)
        elif client.startswith('wheel'):
            low['fun'] = args.pop(0)
            for arg in args:
                if '=' in arg:
                    key, value = arg.split('=', 1)
                    try:
                        low[key] = json.loads(value)
                    except JSONDecodeError:
                        low[key] = value
                else:
                    low.setdefault('args', []).append(arg)
        elif client.startswith('ssh'):
            if len(args) < 2:
                self.parser.error("Command or target not specified")

            low['tgt_type'] = self.options.expr_form
            low['tgt'] = args.pop(0)
            low['fun'] = args.pop(0)
            low['batch'] = self.options.batch
            low['arg'] = args
        else:
            raise PepperException('Client not implemented: {0}'.format(client))

        return [low]
Ejemplo n.º 6
0
    def req(self, path, data=None):
        '''
        A thin wrapper around urllib2 to send requests and return the response

        If the current instance contains an authentication token it will be
        attached to the request as a custom header.

        :rtype: dictionary

        '''
        if ((hasattr(data, 'get') and data.get('eauth') == 'kerberos')
                or self.auth.get('eauth') == 'kerberos'):
            return self.req_requests(path, data)

        headers = {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'X-Requested-With': 'XMLHttpRequest',
        }

        opener = build_opener()
        for handler in opener.handlers:
            if isinstance(handler, HTTPHandler):
                handler.set_http_debuglevel(self.debug_http)
            if isinstance(handler, HTTPSHandler):
                handler.set_http_debuglevel(self.debug_http)
        install_opener(opener)

        # Build POST data
        if data is not None:
            postdata = json.dumps(data).encode()
            clen = len(postdata)
        else:
            postdata = None

        # Create request object
        url = self._construct_url(path)
        req = Request(url, postdata, headers)

        # Add POST data to request
        if data is not None:
            req.add_header('Content-Length', clen)

        # Add auth header to request
        if self.auth and 'token' in self.auth and self.auth['token']:
            req.add_header('X-Auth-Token', self.auth['token'])

        # Send request
        try:
            if not (self._ssl_verify):
                con = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
                f = urlopen(req, context=con)
            else:
                f = urlopen(req)
            content = f.read().decode('utf-8')
            if (self.debug_http):
                logger.debug('Response: %s', content)
            ret = json.loads(content)
        except (HTTPError, URLError) as exc:
            logger.debug('Error with request', exc_info=True)
            status = getattr(exc, 'code', None)

            if status == 401:
                raise PepperException('Authentication denied')

            if status == 500:
                raise PepperException('Server error.')

            logger.error('Error with request: {0}'.format(exc))
            raise
        except AttributeError:
            logger.debug('Error converting response from JSON', exc_info=True)
            raise PepperException('Unable to parse the server response.')

        return ret
Ejemplo n.º 7
0
    def parse_cmd(self, api):
        '''
        Extract the low data for a command from the passed CLI params
        '''
        # Short-circuit if JSON was given.
        if self.options.json_input:
            try:
                return json.loads(self.options.json_input)
            except JSONDecodeError:
                raise PepperArgumentsException("Invalid JSON given.")

        if self.options.json_file:
            try:
                with open(self.options.json_file, 'r') as json_content:
                    try:
                        return json.load(json_content)
                    except JSONDecodeError:
                        raise PepperArgumentsException("Invalid JSON given.")
            except FileNotFoundError:
                raise PepperArgumentsException('Cannot open file: %s',
                                               self.options.json_file)

        args = list(self.args)

        client = self.options.client if not self.options.batch else 'local_batch'
        low = {'client': client}

        if client.startswith('local'):
            if len(args) < 2:
                self.parser.error("Command or target not specified")

            low['tgt_type'] = self.options.expr_form
            low['tgt'] = args.pop(0)
            low['fun'] = args.pop(0)
            low['batch'] = self.options.batch
            low['arg'] = args
        elif client.startswith('runner'):
            low['fun'] = args.pop(0)
            # post https://github.com/saltstack/salt/pull/50124, kwargs can be
            # passed as is in foo=bar form, splitting and deserializing will
            # happen in salt-api. additionally, the presence of salt-version header
            # means we are neon or newer, so don't need a finer grained check
            if api.salt_version:
                low['arg'] = args
            else:
                for arg in args:
                    if '=' in arg:
                        key, value = arg.split('=', 1)
                        try:
                            low[key] = json.loads(value)
                        except JSONDecodeError:
                            low[key] = value
                    else:
                        low.setdefault('arg', []).append(arg)
        elif client.startswith('wheel'):
            low['fun'] = args.pop(0)
            # see above comment in runner arg handling
            if api.salt_version:
                low['arg'] = args
            else:
                for arg in args:
                    if '=' in arg:
                        key, value = arg.split('=', 1)
                        try:
                            low[key] = json.loads(value)
                        except JSONDecodeError:
                            low[key] = value
                    else:
                        low.setdefault('arg', []).append(arg)
        elif client.startswith('ssh'):
            if len(args) < 2:
                self.parser.error("Command or target not specified")

            low['tgt_type'] = self.options.expr_form
            low['tgt'] = args.pop(0)
            low['fun'] = args.pop(0)
            low['batch'] = self.options.batch
            low['arg'] = args
        else:
            raise PepperException('Client not implemented: {0}'.format(client))

        return [low]