| Chandan Kumar | 5e61987 | 2017-09-07 22:23:55 +0530 | [diff] [blame] | 1 | # Copyright 2011, VMware, Inc. | 
|  | 2 | # All Rights Reserved. | 
|  | 3 | # | 
|  | 4 | #    Licensed under the Apache License, Version 2.0 (the "License"); you may | 
|  | 5 | #    not use this file except in compliance with the License. You may obtain | 
|  | 6 | #    a copy of the License at | 
|  | 7 | # | 
|  | 8 | #         http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 9 | # | 
|  | 10 | #    Unless required by applicable law or agreed to in writing, software | 
|  | 11 | #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | 
|  | 12 | #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | 
|  | 13 | #    License for the specific language governing permissions and limitations | 
|  | 14 | #    under the License. | 
|  | 15 | # | 
|  | 16 | # Borrowed from nova code base, more utilities will be added/borrowed as and | 
|  | 17 | # when needed. | 
|  | 18 |  | 
|  | 19 | """Utilities and helper functions.""" | 
|  | 20 |  | 
| Chandan Kumar | 667d3d3 | 2017-09-22 12:24:06 +0530 | [diff] [blame] | 21 | import threading | 
|  | 22 | import time | 
| zheng.yong | 74e760a | 2019-05-22 14:16:14 +0800 | [diff] [blame] | 23 | try: | 
|  | 24 | import urlparse | 
|  | 25 | except ImportError: | 
|  | 26 | from urllib import parse as urlparse | 
| Chandan Kumar | 667d3d3 | 2017-09-22 12:24:06 +0530 | [diff] [blame] | 27 |  | 
| Brian Haley | 33ef460 | 2018-04-26 14:37:49 -0400 | [diff] [blame] | 28 | import eventlet | 
| Maciej Józefczyk | 328edc8 | 2019-09-16 14:05:48 +0000 | [diff] [blame] | 29 | from tempest.lib import exceptions | 
| Brian Haley | 33ef460 | 2018-04-26 14:37:49 -0400 | [diff] [blame] | 30 |  | 
| zheng.yong | 74e760a | 2019-05-22 14:16:14 +0800 | [diff] [blame] | 31 | SCHEMA_PORT_MAPPING = { | 
|  | 32 | "http": 80, | 
|  | 33 | "https": 443, | 
|  | 34 | } | 
|  | 35 |  | 
| Chandan Kumar | 5e61987 | 2017-09-07 22:23:55 +0530 | [diff] [blame] | 36 |  | 
|  | 37 | class classproperty(object): | 
|  | 38 | def __init__(self, f): | 
|  | 39 | self.func = f | 
|  | 40 |  | 
|  | 41 | def __get__(self, obj, owner): | 
| Chandan Kumar | 667d3d3 | 2017-09-22 12:24:06 +0530 | [diff] [blame] | 42 | return self.func(owner) | 
|  | 43 |  | 
| Chandan Kumar | 5e61987 | 2017-09-07 22:23:55 +0530 | [diff] [blame] | 44 |  | 
|  | 45 | class WaitTimeout(Exception): | 
|  | 46 | """Default exception coming from wait_until_true() function.""" | 
|  | 47 |  | 
|  | 48 |  | 
|  | 49 | class LockWithTimer(object): | 
|  | 50 | def __init__(self, threshold): | 
|  | 51 | self._threshold = threshold | 
|  | 52 | self.timestamp = 0 | 
|  | 53 | self._lock = threading.Lock() | 
|  | 54 |  | 
|  | 55 | def acquire(self): | 
|  | 56 | return self._lock.acquire(False) | 
|  | 57 |  | 
|  | 58 | def release(self): | 
|  | 59 | return self._lock.release() | 
|  | 60 |  | 
|  | 61 | def time_to_wait(self): | 
|  | 62 | return self.timestamp - time.time() + self._threshold | 
|  | 63 |  | 
| Chandan Kumar | 667d3d3 | 2017-09-22 12:24:06 +0530 | [diff] [blame] | 64 |  | 
| Chandan Kumar | 5e61987 | 2017-09-07 22:23:55 +0530 | [diff] [blame] | 65 | def wait_until_true(predicate, timeout=60, sleep=1, exception=None): | 
| Brian Haley | ae328b9 | 2018-10-09 19:51:54 -0400 | [diff] [blame] | 66 | """Wait until callable predicate is evaluated as True | 
|  | 67 |  | 
| Chandan Kumar | 5e61987 | 2017-09-07 22:23:55 +0530 | [diff] [blame] | 68 | :param predicate: Callable deciding whether waiting should continue. | 
|  | 69 | Best practice is to instantiate predicate with functools.partial() | 
|  | 70 | :param timeout: Timeout in seconds how long should function wait. | 
|  | 71 | :param sleep: Polling interval for results in seconds. | 
|  | 72 | :param exception: Exception instance to raise on timeout. If None is passed | 
|  | 73 | (default) then WaitTimeout exception is raised. | 
|  | 74 | """ | 
|  | 75 | try: | 
|  | 76 | with eventlet.Timeout(timeout): | 
|  | 77 | while not predicate(): | 
|  | 78 | eventlet.sleep(sleep) | 
|  | 79 | except eventlet.Timeout: | 
|  | 80 | if exception is not None: | 
| Brian Haley | 8aaa73f | 2018-10-09 19:55:44 -0400 | [diff] [blame] | 81 | # pylint: disable=raising-bad-type | 
| Chandan Kumar | 5e61987 | 2017-09-07 22:23:55 +0530 | [diff] [blame] | 82 | raise exception | 
|  | 83 | raise WaitTimeout("Timed out after %d seconds" % timeout) | 
| Brian Haley | ba80045 | 2017-12-14 10:30:48 -0500 | [diff] [blame] | 84 |  | 
|  | 85 |  | 
| Federico Ressi | 0e04f8f | 2018-10-24 12:19:05 +0200 | [diff] [blame] | 86 | def override_class(overriden_class, overrider_class): | 
|  | 87 | """Override class definition with a MixIn class | 
|  | 88 |  | 
|  | 89 | If overriden_class is not a subclass of overrider_class then it creates | 
|  | 90 | a new class that has as bases overrider_class and overriden_class. | 
|  | 91 | """ | 
|  | 92 |  | 
|  | 93 | if not issubclass(overriden_class, overrider_class): | 
|  | 94 | name = overriden_class.__name__ | 
|  | 95 | bases = (overrider_class, overriden_class) | 
|  | 96 | overriden_class = type(name, bases, {}) | 
|  | 97 | return overriden_class | 
| zheng.yong | 74e760a | 2019-05-22 14:16:14 +0800 | [diff] [blame] | 98 |  | 
|  | 99 |  | 
|  | 100 | def normalize_url(url): | 
|  | 101 | """Normalize url without port with schema default port | 
|  | 102 |  | 
|  | 103 | """ | 
|  | 104 | parse_result = urlparse.urlparse(url) | 
|  | 105 | (scheme, netloc, url, params, query, fragment) = parse_result | 
|  | 106 | port = parse_result.port | 
|  | 107 | if scheme in SCHEMA_PORT_MAPPING and not port: | 
|  | 108 | netloc = netloc + ":" + str(SCHEMA_PORT_MAPPING[scheme]) | 
|  | 109 | return urlparse.urlunparse((scheme, netloc, url, params, query, fragment)) | 
| Maciej Józefczyk | 328edc8 | 2019-09-16 14:05:48 +0000 | [diff] [blame] | 110 |  | 
|  | 111 |  | 
|  | 112 | def kill_nc_process(ssh_client): | 
|  | 113 | cmd = "killall -q nc" | 
|  | 114 | try: | 
|  | 115 | ssh_client.exec_command(cmd) | 
|  | 116 | except exceptions.SSHExecCommandFailed: | 
|  | 117 | pass | 
|  | 118 |  | 
|  | 119 |  | 
|  | 120 | def spawn_http_server(ssh_client, port, message): | 
|  | 121 | cmd = ("(echo -e 'HTTP/1.1 200 OK\r\n'; echo '%(msg)s') " | 
|  | 122 | "| sudo nc -lp %(port)d &" % {'msg': message, 'port': port}) | 
|  | 123 | ssh_client.exec_command(cmd) | 
|  | 124 |  | 
|  | 125 |  | 
|  | 126 | def call_url_remote(ssh_client, url): | 
|  | 127 | cmd = "curl %s --retry 3 --connect-timeout 2" % url | 
|  | 128 | return ssh_client.exec_command(cmd) |