示例#1
0
    def login(self, username, password):
        """
        Login to the Traffic Ops API.
        :param username: Traffic Ops User Name
        :type username: Text
        :param password: Traffic Ops User Password
        :type password: Text
        :return: None
        :rtype: None
        :raises: trafficops.restapi.LoginError
        """

        if not self.is_open:
            self.create()

        self._logged_in = False
        try:
            # Try to login to Traffic Ops
            self.post(u'user/login', data={u'u': username, u'p': password})
            self._logged_in = True
        except rex.SSLError as e:
            self.close()
            msg = (u'{0}.  This system may have a self-signed certificate.  Try creating this TOSession '
                   u'object passing verify_cert=False. e.g. TOSession(..., verify_cert=False). '
                   u'WARNING: disabling certificate verification is not recommended.')
            msg = msg.format(e)
            utils.log_with_debug_info(logging.ERROR, msg)
            raise restapi.LoginError(msg)
        except restapi.OperationError as e:
            msg = u'Logging in to Traffic Ops has failed. Reason: {0}'
            msg = msg.format(e)
            self.close()
            utils.log_with_debug_info(logging.ERROR, msg)
            raise restapi.OperationError(msg)
示例#2
0
    def __init__(self, host_ip, host_port=443, api_version=u'1.3', ssl=True, headers=default_headers,
                 verify_cert=True):
        """
        The class initializer.
        :param host_ip: The dns name or ip address of the Traffic Ops host to use to talk to the API
        :type host_ip: Text
        :param host_port: The port to use when contacting the Traffic Ops API
        :type host_port: int
        :param api_version: The version of the API to use when calling end-points on the Traffic Ops API
        :type api_version: Text
        :param ssl: Should ssl be used? http vs. https
        :type ssl: bool
        :param headers:  The http headers to use when contacting the Traffic Ops API
        :type headers: Dict[Text, Text]
        :param verify_cert: Should the ssl certificates be verified when contacting the Traffic Ops API.
                            You may want to set this to False for systems with self-signed certificates.
        :type verify_cert: bool
        """
        super(TOSession, self).__init__(host_ip=host_ip, api_version=api_version,
                                        api_base_path=u'api/{api_version}/',
                                        host_port=host_port, ssl=ssl, headers=headers, verify_cert=verify_cert)

        self._logged_in = False

        msg = u'TOSession instance {0:#0x} initialized: Details: {1}'
        utils.log_with_debug_info(logging.DEBUG, msg.format(id(self), self.__dict__))
示例#3
0
    def close(self):
        """
		Close and cleanup the requests Session object.
		:return: None
		:rtype: None
		"""

        if self._session:
            sid = id(self._session)
            self._session.close()
            del self._session
            self._session = None

            if logging:
                msg = u'Internal requests Session instance 0x{0:x} closed and cleaned up'
                utils.log_with_debug_info(logging.DEBUG, msg.format(sid))
示例#4
0
        def method_wrapper(self, *args, **kwargs):
            # Positional arguments, e.g. *args, are not being used. Keyword arguments are the
            # preferred way to pass the parameters needed by the helper functions
            if (self.api_version is None) or (self.api_version in supported_versions):
                msg = (u'Calling method [{0}] with keyword arguments [{1}] '
                       u'via API endpoint method [{2}]')  # type: Text
                utils.log_with_debug_info(logging.DEBUG, msg.format(method_name, kwargs, func.__name__))

                return getattr(self, method_name)(api_path, **kwargs)
            else:
                # Client API version is not supported by the method being called
                msg = (u"Method [{0}] is not supported by this client's API version [{1}]; "
                       u'Supported versions: {2}')  # type: Text
                msg = msg.format(func.__name__, self.api_version, supported_versions)
                utils.log_with_debug_info(logging.DEBUG, msg)
                raise OperationError(msg)
示例#5
0
    def close(self):
        """
        Close and cleanup the requests Session object.
        :return: None
        :rtype: None
        """

        if self._session:
            sid = id(self._session)
            self._session.close()
            del self._session
            self._session = None

            if logging:
                msg = u'Internal requests Session instance 0x{0:x} closed and cleaned up'
                utils.log_with_debug_info(logging.DEBUG, msg.format(sid))
示例#6
0
    def create(self):
        """
        Create the requests.Session to communicate with the RESTful API.
        :return: None
        :rtype: None
        """
        if self._session:
            self.close()

        if not self._session:
            self._session = requests.Session()
            self._session.mount('http://', ra.HTTPAdapter(max_retries=self._max_retries))
            self._session.mount('https://', ra.HTTPAdapter(max_retries=self._max_retries))

            msg = u'Created internal requests Session instance {0:#0x}'
            utils.log_with_debug_info(logging.DEBUG, msg.format(id(self._session)))
示例#7
0
    def create(self):
        """
		Create the requests.Session to communicate with the RESTful API.
		:return: None
		:rtype: None
		"""
        if self._session:
            self.close()

        if not self._session:
            self._session = requests.Session()
            self._session.mount('http://',
                                ra.HTTPAdapter(max_retries=self._max_retries))
            self._session.mount('https://',
                                ra.HTTPAdapter(max_retries=self._max_retries))

            msg = u'Created internal requests Session instance {0:#0x}'
            utils.log_with_debug_info(logging.DEBUG,
                                      msg.format(id(self._session)))
示例#8
0
        def method_wrapper(self, *args, **kwargs):
            # Positional arguments, e.g. *args, are not being used. Keyword arguments are the
            # preferred way to pass the parameters needed by the helper functions
            if (self.api_version is None) or (self.api_version
                                              in supported_versions):
                msg = (u'Calling method [{0}] with keyword arguments [{1}] '
                       u'via API endpoint method [{2}]')  # type: Text
                utils.log_with_debug_info(
                    logging.DEBUG,
                    msg.format(method_name, kwargs, func.__name__))

                return getattr(self, method_name)(api_path, **kwargs)
            else:
                # Client API version is not supported by the method being called
                msg = (
                    u"Method [{0}] is not supported by this client's API version [{1}]; "
                    u'Supported versions: {2}')  # type: Text
                msg = msg.format(func.__name__, self.api_version,
                                 supported_versions)
                utils.log_with_debug_info(logging.DEBUG, msg)
                raise OperationError(msg)
示例#9
0
    def login(self, username, password):
        """
        Login to the Traffic Ops API.
        :param username: Traffic Ops User Name
        :type username: Text
        :param password: Traffic Ops User Password
        :type password: Text
        :return: None
        :rtype: None
        :raises: trafficops.restapi.LoginError
        """

        if not self.is_open:
            self.create()

        self._logged_in = False
        try:
            # Try to login to Traffic Ops
            self.post(u'user/login', data={u'u': username, u'p': password})
            self._logged_in = True
        except rex.SSLError as e:
            self.close()
            msg = (
                u'{0}.  This system may have a self-signed certificate.  Try creating this TOSession '
                u'object passing verify_cert=False. e.g. TOSession(..., verify_cert=False). '
                u'WARNING: disabling certificate verification is not recommended.'
            )
            msg = msg.format(e)
            utils.log_with_debug_info(logging.ERROR, msg)
            raise restapi.LoginError(msg)
        except restapi.OperationError as e:
            msg = u'Logging in to Traffic Ops has failed. Reason: {0}'
            msg = msg.format(e)
            self.close()
            utils.log_with_debug_info(logging.ERROR, msg)
            raise restapi.OperationError(msg)
示例#10
0
    def __init__(self,
                 host_ip,
                 host_port=443,
                 api_version=u'1.2',
                 ssl=True,
                 headers=default_headers,
                 verify_cert=True):
        """
        The class initializer.
        :param host_ip: The dns name or ip address of the Traffic Ops host to use to talk to the API
        :type host_ip: Text
        :param host_port: The port to use when contacting the Traffic Ops API
        :type host_port: int
        :param api_version: The version of the API to use when calling end-points on the Traffic Ops API
        :type api_version: Text
        :param ssl: Should ssl be used? http vs. https
        :type ssl: bool
        :param headers:  The http headers to use when contacting the Traffic Ops API
        :type headers: Dict[Text, Text]
        :param verify_cert: Should the ssl certificates be verified when contacting the Traffic Ops API.
                            You may want to set this to False for systems with self-signed certificates.
        :type verify_cert: bool
        """
        super(TOSession, self).__init__(host_ip=host_ip,
                                        api_version=api_version,
                                        api_base_path=u'api/{api_version}/',
                                        host_port=host_port,
                                        ssl=ssl,
                                        headers=headers,
                                        verify_cert=verify_cert)

        self._logged_in = False

        msg = u'TOSession instance {0:#0x} initialized: Details: {1}'
        utils.log_with_debug_info(logging.DEBUG,
                                  msg.format(id(self), self.__dict__))
示例#11
0
    def _do_operation(self,
                      operation,
                      api_path,
                      query_params=None,
                      munchify=True,
                      debug_response=False,
                      expected_status_codes=(
                          200,
                          204,
                      ),
                      *args,
                      **kwargs):
        """
		Helper method to perform http operation requests - This is a boilerplate process for http operations
		:param operation: Name of method to call on the self._session object to perform the http request
		:type operation: Text
		:param api_path: The path to the API end-point that you want to call which does not include the
						 base url.  e.g. 'user/login', 'servers', etc.  This string can contain substitution
						 parameters as denoted by a valid field_name replacement field specification as per
						 str.format().
							 E.g. 'cachegroups/{id}' or 'cachegroups/{id:d}'
		:type api_path: Text
		:param: query_params: URL query params to provide to the end-point.
							  E.g. { 'sort': 'asc', 'maxresults': 200 }  which
							  translates to something like '?sort=asc&maxresults=200' which
							  is appended to the request URL
		:type query_params: Union[Dict[Text, Any], None]
		:param: munchify: If True encapsulate data to be returned in a munch.Munch object which allows
						  keys in a Python dictionary to additionally have attribute access.
							  E.g. a_dict['a_key'] with munch becomes a_dict['a_key'] or a_dict.a_key
		:type munchify: bool
		:param kwargs: Passed Keyword Parameters.  If you need to send JSON data to the endpoint pass the
					   keyword parameter 'data' with the python data structure e.g. a dict.  This method
					   will convert it to JSON before sending it to the API endpoint.
		:type kwargs: Dict[Text, Any]
		:param debug_response: If True, the actual response data text will be added to the log if a JSON decoding
							   exception is encountered.
		:type debug_response: bool
		:type expected_status_codes: Tuple[int]
		:param: expected_status_codes: expected success http status codes.  If the user needs to override
									   the defaults this parameter can be passed.
									   E.g. (200, 204,)
		:type munchify: bool

		:return: Python data structure distilled from JSON from the API request.
		:rtype: Tuple[Union[Dict[Text, Any], List[Dict[Text, Any]], munch.Munch, List[munch.Munch]],
					  requests.Response]
		:raises: miscellaneous.exceptions.OperationError
		"""

        if not self._session:
            msg = u'No session has been created for the API.  Have you called create() yet?'
            utils.log_with_debug_info(logging.ERROR, msg)
            raise OperationError(msg)

        response = None
        retdata = None

        endpoint = self._build_endpoint(api_path,
                                        params=kwargs,
                                        query_params=query_params)

        params = {
            u'headers': self._headers,
            u'verify': self._verify_cert,
        }

        if u'data' in kwargs:
            params[u'data'] = json.dumps(kwargs[u'data'])

        utils.log_with_debug_info(logging.DEBUG,
                                  u'Call parameters: {0}'.format(params))

        # Call the API endpoint
        response = getattr(self._session, operation)(endpoint, **params)

        utils.log_with_debug_info(
            logging.DEBUG,
            u'Response status: {0} {1}'.format(response.status_code,
                                               response.reason))

        if response.status_code not in expected_status_codes:
            try:
                retdata = response.json()
            except Exception as e:
                # Invalid JSON payload.
                msg = (
                    u'HTTP Status Code: [{0}]; API response data for end-point [{1}] does not '
                    u'appear to be valid JSON. Cause: {2}.')
                msg = msg.format(response.status_code, endpoint, e)
                if debug_response:
                    utils.log_with_debug_info(
                        logging.ERROR,
                        msg + u' Data: [' + str(response.text) + u']')
                raise InvalidJSONError(msg)
            msg = u'{0} request to RESTful API at [{1}] expected status(s) {2}; failed: {3} {4}; Response: {5}'
            msg = msg.format(operation.upper(), endpoint,
                             expected_status_codes, response.status_code,
                             response.reason, retdata)
            utils.log_with_debug_info(logging.ERROR, msg)
            raise OperationError(msg)

        try:
            if response.status_code in ('204', ):
                # "204 No Content"
                retdata = {}
            else:
                # Decode the expected JSON
                retdata = response.json()
        except Exception as e:
            # Invalid JSON payload.
            msg = (
                u'HTTP Status Code: [{0}]; API response data for end-point [{1}] does not '
                u'appear to be valid JSON. Cause: {2}.')
            msg = msg.format(response.status_code, endpoint, e)
            if debug_response:
                utils.log_with_debug_info(
                    logging.ERROR,
                    msg + u' Data: [' + str(response.text) + u']')
            raise InvalidJSONError(msg)
        retdata = munch.munchify(retdata) if munchify else retdata
        return (retdata[u'response']
                if u'response' in retdata else retdata), response
示例#12
0
    def _build_endpoint(self, api_path, params=None, query_params=None):
        """
		Helper function to form API URL.
		The base url is '<protocol>://<hostname>[:<port>]/<api base url>'
			E.g. 'https://to.somedomain.net/api/0.1/'
		:param api_path: The path to the API end-point that you want to call which does not include the
						 base url.  e.g. 'user/login', 'servers', etc.  This string can contain
						 substitution parameters as denoted by a valid field_name
						 replacement field specification as per str.format().
						   E.g. 'cachegroups/{id}' or 'cachegroups/{id:d}'
		:type api_path: Text
		:param params: If str.format() field_name replacement field specifications exists in the
					   api_path use this dictionary to perform replacements of the specifications
					   with the value(s) in the dictionary that match the parameter name(s).
					   E.g. '{param_id}' or '{param_id:d}' in api_string is replaced by value in
							params['param_id'].
		:type params: Union[Dict[Text, Any], None]
		:param: query_params: URL query params to provide to the end-point.
							 E.g. { 'sort': 'asc', 'maxresults': 200 }  which
							 translates to something like '?sort=asc&maxresults=200' which
							 is appended to the request URL
		:type query_params: Union[Dict[Text, Any], None]
		:return: The base url plus the passed and possibly substituted api_path to form a
				 complete URL to the API resource to request
		:rtype: Text
		:raises: ValueError
		"""

        new_api_path = api_path

        # Replace all parameters in the new_api_path path, if required
        try:
            # Make the parameters values safe for adding to URLs
            url_params = {
                k: compat.quote(str(v)) if isinstance(v, str) else v
                for k, v in iteritems(params)
            }

            utils.log_with_debug_info(
                logging.DEBUG, u'URL parameters are: [{0}]'.format(url_params))

            qparams = u''
            if query_params:
                # Process the URL query parameters
                qparams = u'?{0}'.format(compat.urlencode(query_params))
                utils.log_with_debug_info(
                    logging.DEBUG,
                    u'URL query parameters are: [{0}]'.format(qparams))

            new_api_path = api_path.format(**url_params) + qparams
        except KeyError as e:
            msg = (
                u'Expecting a value for keyword argument [{0}] for format field '
                u'specification [{1!r}]')
            msg = msg.format(e, api_path)
            utils.log_with_debug_info(logging.ERROR, msg)
            raise ValueError(msg)
        except ValueError as e:
            msg = (
                u'One or more values do not match the format field specification '
                u'[{0!r}]; Supplied values: {1!r} ')
            msg = msg.format(api_path, params)
            utils.log_with_debug_info(logging.ERROR, msg)
            raise ValueError(msg)

        retval = compat.urljoin(self.api_base_url, new_api_path)

        utils.log_with_debug_info(
            logging.DEBUG, u'Built end-point to return: {0}'.format(retval))

        return retval
示例#13
0
    def __init__(self,
                 host_ip,
                 api_version=None,
                 api_base_path=u'api/',
                 host_port=443,
                 ssl=True,
                 headers=default_headers,
                 verify_cert=True,
                 create_session=False,
                 max_retries=5):
        """
		The class initializer.
		:param host_ip: The dns name or ip address of the RESTful API host to use to talk to the API
		:type host_ip: Text
		:param host_port: The port to use when contacting the RESTful API
		:type host_port: int
		:param api_version: The version of the API to make calls against.  If supplied,
							end-point version validation will be performed.  If supplied as
							None, no version validation will be performed.  None is allowed
							so that non-versioned REST APIs can be implemented.
							   E.g. '1.2', None, etc.
		:type api_version: Union[Text, None]
		:param api_base_path: The part of the url that is the base path, from the web server root
							  (which may include a api version), for all api end-points without
							  the server url portion.
								  E.g. 'api/', 'api/1.2/', etc.

							  NOTE: To specify the base path with the passed 'api_version' you can
									specify api_base_path as 'api/{api_version}/' and the API
									version will be substituted.  If api_version is None and
									'{api_version}' is specified in the api_base_path string
									then an exception will be thrown.
									e.g. api_version=u'1.2' -> 'api/{api_version}/' -> 'api/1.2/'
										 api_version=None   -> 'api/{api_version}/' -> Throws Exception
		:type api_base_path: Text
		:param ssl: Should ssl be used? http vs. https
		:type ssl: bool
		:param headers:  The http headers to use when contacting the RESTful API
		:type headers: Dict[Text, Text]
		:param verify_cert: Should the ssl certificates be verified when contacting the RESTful API.
							You may want to set this to False for systems with self-signed certificates.
		:type verify_cert: bool
		:param create_session: Should a session be created automatically?
		:type create_session: bool
		"""

        self._session = None
        self._host_ip = host_ip
        self._host_port = host_port
        self._api_version = api_version
        self._api_base_path = api_base_path
        self._ssl = ssl
        self._headers = headers
        self._verify_cert = verify_cert
        self._create_session = create_session
        self._max_retries = max_retries

        # Setup API End-point Version validation, if enabled
        self.__api_version_format_name = u'api_version'
        self.__api_version_format_value = u'{{{0}}}'.format(
            self.__api_version_format_name)

        if self._api_version:
            # if api_base_path is supplied as 'api/{api_version}/' or some string
            # containing '{api_version}' then try to substitute the api_version supplied
            # by the user.

            version_params = {
                self.__api_version_format_name: self._api_version
            }
            self._api_base_path = self._api_base_path.format(**version_params)

        if not self._api_version and self.__api_version_format_value in self._api_base_path:
            msg = (
                u'{0} was specified in the API Base Path [{1}] '
                u'but the replacement did not occur because the API Version '
                u'was not supplied.')  # type: Text
            msg = msg.format(self.__api_version_format_value,
                             self._api_base_path)
            utils.log_with_debug_info(logging.ERROR, msg)
            raise OperationError(msg)

        # Setup some common URLs
        self._server_url = u'{0}://{1}{2}/'.format(
            u'https' if ssl else u'http', host_ip,
            u':{0}'.format(host_port) if host_port else u'')
        self._api_base_url = compat.urljoin(self._server_url,
                                            self._api_base_path)
        self._api_base_url = self._api_base_url.rstrip(u'/') + u'/'

        utils.log_with_debug_info(logging.DEBUG,
                                  u'Server URL: {0}'.format(self._server_url))
        utils.log_with_debug_info(
            logging.DEBUG, u'API Base Path: {0}'.format(self._api_base_path))
        utils.log_with_debug_info(
            logging.DEBUG, u'API Version: {0}'.format(self._api_version))
        utils.log_with_debug_info(
            logging.DEBUG, u'API Base URL: {0}'.format(self._api_base_url))

        if not self._verify_cert:
            # Not verifying certs so let's disable the warning
            import requests.packages.urllib3 as u3l
            import requests.packages.urllib3.exceptions as u3e
            u3l.disable_warnings(u3e.InsecureRequestWarning)
            utils.log_with_debug_info(
                logging.WARNING,
                u'Certificate verification warnings are disabled.')

        msg = u'RestApiSession instance {0:#0x} initialized: Details: {1}'
        utils.log_with_debug_info(logging.DEBUG,
                                  msg.format(id(self), self.__dict__))

        if self._create_session:
            self.create()
示例#14
0
    def _do_operation(self, operation, api_path, query_params=None, munchify=True, debug_response=False,
                      expected_status_codes=(200, 204,), *args, **kwargs):
        """
        Helper method to perform http operation requests - This is a boilerplate process for http operations
        :param operation: Name of method to call on the self._session object to perform the http request
        :type operation: Text
        :param api_path: The path to the API end-point that you want to call which does not include the
                         base url.  e.g. 'user/login', 'servers', etc.  This string can contain substitution
                         parameters as denoted by a valid field_name replacement field specification as per
                         str.format(). 
                             E.g. 'cachegroups/{id}' or 'cachegroups/{id:d}'
        :type api_path: Text
        :param: query_params: URL query params to provide to the end-point.
                              E.g. { 'sort': 'asc', 'maxresults': 200 }  which
                              translates to something like '?sort=asc&maxresults=200' which
                              is appended to the request URL
        :type query_params: Union[Dict[Text, Any], None]
        :param: munchify: If True encapsulate data to be returned in a munch.Munch object which allows
                          keys in a Python dictionary to additionally have attribute access.
                              E.g. a_dict['a_key'] with munch becomes a_dict['a_key'] or a_dict.a_key
        :type munchify: bool
        :param kwargs: Passed Keyword Parameters.  If you need to send JSON data to the endpoint pass the 
                       keyword parameter 'data' with the python data structure e.g. a dict.  This method
                       will convert it to JSON before sending it to the API endpoint.
        :type kwargs: Dict[Text, Any]
        :param debug_response: If True, the actual response data text will be added to the log if a JSON decoding
                               exception is encountered.
        :type debug_response: bool
        :type expected_status_codes: Tuple[int]
        :param: expected_status_codes: expected success http status codes.  If the user needs to override
                                       the defaults this parameter can be passed.
                                       E.g. (200, 204,)
        :type munchify: bool

        :return: Python data structure distilled from JSON from the API request.
        :rtype: Tuple[Union[Dict[Text, Any], List[Dict[Text, Any]], munch.Munch, List[munch.Munch]],
                      requests.Response]
        :raises: miscellaneous.exceptions.OperationError
        """

        if not self._session:
            msg = u'No session has been created for the API.  Have you called create() yet?'
            utils.log_with_debug_info(logging.ERROR, msg)
            raise OperationError(msg)

        response = None
        retdata = None

        endpoint = self._build_endpoint(api_path, params=kwargs, query_params=query_params)

        params = {
            u'headers': self._headers,
            u'verify': self._verify_cert,
        }

        if u'data' in kwargs:
            params[u'data'] = json.dumps(kwargs[u'data'])

        utils.log_with_debug_info(logging.DEBUG, u'Call parameters: {0}'.format(params))

        # Call the API endpoint
        response = getattr(self._session, operation)(endpoint, **params)

        utils.log_with_debug_info(logging.DEBUG, u'Response status: {0} {1}'.format(response.status_code,
                                  response.reason))

        if response.status_code not in expected_status_codes:
            try:
                retdata = response.json()
            except Exception as e:
                # Invalid JSON payload.
                msg = (u'HTTP Status Code: [{0}]; API response data for end-point [{1}] does not '
                       u'appear to be valid JSON. Cause: {2}.')
                msg = msg.format(response.status_code, endpoint, e)
                if debug_response:
                    utils.log_with_debug_info(logging.ERROR, msg + u' Data: [' + str(response.text) + u']')
                raise InvalidJSONError(msg)
            msg = u'{0} request to RESTful API at [{1}] expected status(s) {2}; failed: {3} {4}; Response: {5}'
            msg = msg.format(operation.upper(), endpoint, expected_status_codes,
                             response.status_code, response.reason, retdata)
            utils.log_with_debug_info(logging.ERROR, msg)
            raise OperationError(msg)

        try:
            if response.status_code in ('204',):
                # "204 No Content"
                retdata = {}
            else:
                # Decode the expected JSON
                retdata = response.json()
        except Exception as e:
            # Invalid JSON payload.
            msg = (u'HTTP Status Code: [{0}]; API response data for end-point [{1}] does not '
                   u'appear to be valid JSON. Cause: {2}.')
            msg = msg.format(response.status_code, endpoint, e)
            if debug_response:
                utils.log_with_debug_info(logging.ERROR, msg + u' Data: [' + str(response.text) + u']')
            raise InvalidJSONError(msg)
        retdata = munch.munchify(retdata) if munchify else retdata
        return (retdata[u'response'] if u'response' in retdata else retdata), response
示例#15
0
    def _build_endpoint(self, api_path, params=None, query_params=None):
        """
        Helper function to form API URL.  
        The base url is '<protocol>://<hostname>[:<port>]/<api base url>'
            E.g. 'https://to.somedomain.net/api/0.1/'
        :param api_path: The path to the API end-point that you want to call which does not include the
                         base url.  e.g. 'user/login', 'servers', etc.  This string can contain
                         substitution parameters as denoted by a valid field_name 
                         replacement field specification as per str.format(). 
                           E.g. 'cachegroups/{id}' or 'cachegroups/{id:d}'
        :type api_path: Text
        :param params: If str.format() field_name replacement field specifications exists in the
                       api_path use this dictionary to perform replacements of the specifications
                       with the value(s) in the dictionary that match the parameter name(s). 
                       E.g. '{param_id}' or '{param_id:d}' in api_string is replaced by value in
                            params['param_id'].
        :type params: Union[Dict[Text, Any], None]
        :param: query_params: URL query params to provide to the end-point.
                             E.g. { 'sort': 'asc', 'maxresults': 200 }  which
                             translates to something like '?sort=asc&maxresults=200' which
                             is appended to the request URL
        :type query_params: Union[Dict[Text, Any], None]
        :return: The base url plus the passed and possibly substituted api_path to form a
                 complete URL to the API resource to request
        :rtype: Text
        :raises: ValueError
        """

        new_api_path = api_path

        # Replace all parameters in the new_api_path path, if required
        try:
            # Make the parameters values safe for adding to URLs
            url_params = {k: compat.quote(str(v)) if isinstance(v, str) else v for k, v in iteritems(params)}

            utils.log_with_debug_info(logging.DEBUG, u'URL parameters are: [{0}]'.format(url_params))

            qparams = u''
            if query_params:
                # Process the URL query parameters
                qparams = u'?{0}'.format(compat.urlencode(query_params))
                utils.log_with_debug_info(logging.DEBUG, u'URL query parameters are: [{0}]'.format(qparams))

            new_api_path = api_path.format(**url_params) + qparams
        except KeyError as e:
            msg = (u'Expecting a value for keyword argument [{0}] for format field '
                   u'specification [{1!r}]')
            msg = msg.format(e, api_path)
            utils.log_with_debug_info(logging.ERROR, msg)
            raise ValueError(msg)
        except ValueError as e:
            msg = (u'One or more values do not match the format field specification '
                   u'[{0!r}]; Supplied values: {1!r} ')
            msg = msg.format(api_path, params)
            utils.log_with_debug_info(logging.ERROR, msg)
            raise ValueError(msg)

        retval = compat.urljoin(self.api_base_url, new_api_path)

        utils.log_with_debug_info(logging.DEBUG, u'Built end-point to return: {0}'.format(retval))

        return retval
示例#16
0
    def __init__(self, host_ip, api_version=None, api_base_path=u'api/',
                 host_port=443, ssl=True, headers=default_headers, verify_cert=True,
                 create_session=False, max_retries=5):
        """
        The class initializer.
        :param host_ip: The dns name or ip address of the RESTful API host to use to talk to the API
        :type host_ip: Text
        :param host_port: The port to use when contacting the RESTful API
        :type host_port: int
        :param api_version: The version of the API to make calls against.  If supplied,
                            end-point version validation will be performed.  If supplied as
                            None, no version validation will be performed.  None is allowed
                            so that non-versioned REST APIs can be implemented.
                               E.g. '1.2', None, etc.
        :type api_version: Union[Text, None]
        :param api_base_path: The part of the url that is the base path, from the web server root
                              (which may include a api version), for all api end-points without
                              the server url portion.
                                  E.g. 'api/', 'api/1.2/', etc.

                              NOTE: To specify the base path with the passed 'api_version' you can
                                    specify api_base_path as 'api/{api_version}/' and the API
                                    version will be substituted.  If api_version is None and
                                    '{api_version}' is specified in the api_base_path string
                                    then an exception will be thrown.
                                    e.g. api_version=u'1.2' -> 'api/{api_version}/' -> 'api/1.2/'
                                         api_version=None   -> 'api/{api_version}/' -> Throws Exception
        :type api_base_path: Text
        :param ssl: Should ssl be used? http vs. https
        :type ssl: bool
        :param headers:  The http headers to use when contacting the RESTful API
        :type headers: Dict[Text, Text]
        :param verify_cert: Should the ssl certificates be verified when contacting the RESTful API.
                            You may want to set this to False for systems with self-signed certificates.
        :type verify_cert: bool
        :param create_session: Should a session be created automatically?
        :type create_session: bool
        """

        self._session = None
        self._host_ip = host_ip
        self._host_port = host_port
        self._api_version = api_version
        self._api_base_path = api_base_path
        self._ssl = ssl
        self._headers = headers
        self._verify_cert = verify_cert
        self._create_session = create_session
        self._max_retries = max_retries

        # Setup API End-point Version validation, if enabled
        self.__api_version_format_name = u'api_version'
        self.__api_version_format_value = u'{{{0}}}'.format(self.__api_version_format_name)

        if self._api_version:
            # if api_base_path is supplied as 'api/{api_version}/' or some string
            # containing '{api_version}' then try to substitute the api_version supplied
            # by the user.

            version_params = {
                self.__api_version_format_name: self._api_version
            }
            self._api_base_path = self._api_base_path.format(**version_params)

        if not self._api_version and self.__api_version_format_value in self._api_base_path:
            msg = (u'{0} was specified in the API Base Path [{1}] '
                   u'but the replacement did not occur because the API Version '
                   u'was not supplied.')  # type: Text
            msg = msg.format(self.__api_version_format_value, self._api_base_path)
            utils.log_with_debug_info(logging.ERROR, msg)
            raise OperationError(msg)

        # Setup some common URLs
        self._server_url = u'{0}://{1}{2}/'.format(u'https' if ssl else u'http',
                                                   host_ip,
                                                   u':{0}'.format(host_port) if host_port else u'')
        self._api_base_url = compat.urljoin(self._server_url, self._api_base_path)
        self._api_base_url = self._api_base_url.rstrip(u'/') + u'/'

        utils.log_with_debug_info(logging.DEBUG, u'Server URL: {0}'.format(self._server_url))
        utils.log_with_debug_info(logging.DEBUG, u'API Base Path: {0}'.format(self._api_base_path))
        utils.log_with_debug_info(logging.DEBUG, u'API Version: {0}'.format(self._api_version))
        utils.log_with_debug_info(logging.DEBUG, u'API Base URL: {0}'.format(self._api_base_url))

        if not self._verify_cert:
            # Not verifying certs so let's disable the warning
            import requests.packages.urllib3 as u3l
            import requests.packages.urllib3.exceptions as u3e
            u3l.disable_warnings(u3e.InsecureRequestWarning)
            utils.log_with_debug_info(logging.WARNING, u'Certificate verification warnings are disabled.')

        msg = u'RestApiSession instance {0:#0x} initialized: Details: {1}'
        utils.log_with_debug_info(logging.DEBUG, msg.format(id(self), self.__dict__))

        if self._create_session:
            self.create()