def __init__(self, conf: K2hr3Conf) -> None: """Initializes attributes. :param conf: K2hr3Conf object. :type K2hr3Conf: K2hr3Conf :raises K2hr3UserAgentError: api_url validation error. """ # api_url validated for myself. if isinstance(conf, K2hr3Conf) is False: raise _K2hr3UserAgentError( 'conf is a K2hr3Conf instance, not {}'.format(type(conf))) try: _K2hr3UserAgent.validate_url(conf.k2hr3.api_url) except _K2hr3UserAgentError as error: raise _K2hr3UserAgentError( 'a valid url is expected, not {}'.format( conf.k2hr3.api_url)) from error self._conf = conf self._url = conf.k2hr3.api_url # other params validated in oslo_config. self._retries = conf.k2hr3.max_retries self._allow_self_signed_cert = conf.k2hr3.allow_self_signed_cert # init the others. self._ips = [] # type: List[str] self._instance_id = '' self._method = 'DELETE' self._params = {'extra': 'openstack-auto-v1'} self._headers = { 'User-Agent': 'Python-k2hr3_ua/{}.{}'.format(sys.version_info[0], sys.version_info[1]) } self._response = _K2hr3HttpResponse() LOG.debug('useragent initialized.')
def test_notification_endpoint_info_requeue_test_on_exception(self): """Checks if info works correctly. NotificationEndpoint::__call_r3api returns REQUEUE in this case. NotificationEndpoint::info() --> NotificationEndpoint::__call_r3api() --> _K2hr3UserAgent::send() # we mock this method. """ # _K2hr3UserAgent::send() is expected to raise _K2hr3UserAgentError. _K2hr3UserAgent.send = MagicMock( side_effect=_K2hr3UserAgentError('send error')) conf = K2hr3Conf(conf_file_path) conf.k2hr3.requeue_on_error = True # overwrites it True. endpoint = K2hr3NotificationEndpoint(conf) json_file_path = Path( os.sep.join( [here, '..', 'tools', 'data', 'notifications_neutron.json'])).resolve() with json_file_path.open() as fp: data = json.load(fp) result = endpoint.info(data['ctxt'], data['publisher_id'], data['event_type'], data['payload'], data['metadata']) # Ensure the__call_r3api is not called. self.assertEqual(result, REQUEUE)
def instance_id(self, value: str) -> None: # publc. """Sets instance id. :param value: instance id :type value: str """ if isinstance(value, str) is False: raise _K2hr3UserAgentError( 'Please pass UUID as a string, not {}'.format(value)) try: if value: uuid.UUID(value) self._instance_id = value except ValueError as error: raise _K2hr3UserAgentError('Invalid UUID, {} {}'.format( value, error)) # Note: # parameter name is 'cuk' when calling r3api. self._params['cuk'] = self._instance_id
def allow_self_signed_cert(self, value: bool) -> None: # public. """Sets the flag of self signed certificate or not. :param value: True if allow self signed certificate to use. :type value: bool """ if isinstance(value, bool): self._allow_self_signed_cert = value else: raise _K2hr3UserAgentError( 'Boolean value expected, not {}'.format(value))
def method(self, value: str) -> None: """Sets the http request method string. :param value: http request method string :type value: str """ if isinstance(value, str) is True: LOG.debug('http request method is %s', value) self._method = value else: raise _K2hr3UserAgentError( 'method should be string, not {}'.format(value))
def error(self, value: str) -> None: # public. """Sets the HTTP error. :param value: HTTP error :type value: str """ # Input validation in public method should be done first. if isinstance(value, str) is True: self._error = value else: raise _K2hr3UserAgentError( 'error should be str, not {}'.format(value))
def code(self, value: int) -> None: # public. """Sets the HTTP status code. :param value: HTTP status code :type value: int """ # Input validation in public method should be done first. if isinstance(value, int) is True: self._code = value else: raise _K2hr3UserAgentError('code should be int, not {}'.format( type(value)))
def ips(self, value: str) -> None: # public. """Sets ip or ips to the ipaddress list. :param value: ipaddress(str or list) :type value: object """ ips = [] # type: List[str] if isinstance(value, list): ips += value elif isinstance(value, str): ips = [value] else: raise _K2hr3UserAgentError( 'ips must be list or str, not {}'.format(value)) for ipaddress in ips: if isinstance(ipaddress, str) is False: raise _K2hr3UserAgentError( 'ip must be str, not {}'.format(ipaddress)) try: # https://github.com/python/cpython/blob/master/Modules/socketmodule.c#L6172 socket.inet_pton(socket.AF_INET, ipaddress) self._ips += [ipaddress] except OSError: LOG.debug('not ip version4 string %s', ipaddress) try: socket.inet_pton(socket.AF_INET6, ipaddress) self._ips += [ipaddress] except OSError as error: LOG.error('neither ip version4 nor version6 string %s %s', ipaddress, error) raise _K2hr3UserAgentError( 'ip must be valid string, not {} {}'.format( ipaddress, error)) self._ips = ips # overwrite LOG.debug('ips=%s', ips) # Note: # parameter name is 'host' when calling r3api. self._params['host'] = json.dumps(self._ips)
def test_notification_endpoint_info_r3api_failed_by_exception(self): """Checks if info works correctly. NotificationEndpoint::info returns HANDLED if __call_r3api returns HANDLED. __call_r3api internally callses _K2hr3UserAgent::send() to call the R3 API. We mock the method to return False without having access to the API. """ conf = K2hr3Conf(src_dir + '/etc/k2hr3_osnl.conf.sample') endpoint = K2hr3NotificationEndpoint(conf) _K2hr3UserAgent.send = MagicMock( side_effect=_K2hr3UserAgentError('send error')) with open(src_dir + '/tools/data/notifications_neutron.json') as fp: data = json.load(fp) result = endpoint.info(data['ctxt'], data['publisher_id'], data['event_type'], data['payload'], data['metadata']) # Ensure the__call_r3api is not called. self.assertEqual(result, HANDLED)
def validate_url(value): """Returns True if given string is a url. :param value: a url like string :type value: str :returns: True if given string is a url. :rtype: bool """ # scheme try: scheme, url_string = value.split('://', maxsplit=2) except ValueError as error: raise _K2hr3UserAgentError( 'scheme should contain ://, not {}'.format(value)) from error if scheme not in ('http', 'https'): raise _K2hr3UserAgentError( 'scheme should be http or http, not {}'.format(scheme)) else: LOG.debug('scheme is %s', scheme) matches = re.match( r'(?P<domain>[\w|\.]+)?(?P<port>:\d{2,5})?(?P<path>[\w|/]*)?', url_string) if matches is None: raise _K2hr3UserAgentError( 'the argument seems not to be a url string, {}'.format(value)) # domain must be resolved. domain = matches.group('domain') if domain is None: raise _K2hr3UserAgentError( 'url contains no domain, {}'.format(value)) try: # https://github.com/python/cpython/blob/master/Modules/socketmodule.c#L5729 ipaddress = socket.gethostbyname(domain) except OSError as error: # resolve failed raise _K2hr3UserAgentError('unresolved domain, {} {}'.format( domain, error)) else: LOG.debug('%s resolved %s', domain, ipaddress) # path(optional) if matches.group('path') is None: raise _K2hr3UserAgentError( 'url contains no path, {}'.format(value)) path = matches.group('path') # port(optional) port = matches.group('port') LOG.debug('url=%s domain=%s port=%s path=%s', value, domain, port, path) return True
def test_notification_endpoint_call_r3api_requeue_on_exception(self): """Checks if call_r3api works correctly. __call_r3api calls the _K2hr3UserAgent::send() to call the R3 API. We mock _K2hr3UserAgent::send() to throws a _K2hr3UserAgentError. If requeue_on_error is true, then the function returns HANDLED. """ # Expected return_value is REQUEUE in this case. self.mock_method.return_value = REQUEUE conf = K2hr3Conf(src_dir + '/etc/k2hr3_osnl.conf.sample') conf.k2hr3.requeue_on_error = True _K2hr3UserAgent.send = MagicMock( side_effect=_K2hr3UserAgentError('error')) endpoint = K2hr3NotificationEndpoint(conf) with open(src_dir + '/tools/data/notifications_neutron.json') as fp: data = json.load(fp) result = endpoint.info(data['ctxt'], data['publisher_id'], data['event_type'], data['payload'], data['metadata']) self.assertEqual(result, REQUEUE) # Reset it. Default return_value is HANDLED. self.mock_method.return_value = HANDLED
def test_notification_endpoint_call_r3api_requeue_on_exception(self): """Checks if call_r3api works correctly. __call_r3api calls the _K2hr3UserAgent::send() to call the R3 API. We mock _K2hr3UserAgent::send() to throws a _K2hr3UserAgentError. If requeue_on_error is true, then the function returns HANDLED. """ # Expected return_value is REQUEUE in this case. _K2hr3UserAgent.send = MagicMock( side_effect=_K2hr3UserAgentError('error')) conf = K2hr3Conf(conf_file_path) conf.k2hr3.requeue_on_error = True # overwrites it True. endpoint = K2hr3NotificationEndpoint(conf) json_file_path = Path( os.sep.join( [here, '..', 'tools', 'data', 'notifications_neutron.json'])).resolve() with json_file_path.open() as fp: data = json.load(fp) result = endpoint.info(data['ctxt'], data['publisher_id'], data['event_type'], data['payload'], data['metadata']) self.assertEqual(result, REQUEUE)