helpers.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import fnmatch
  2. import functools
  3. import os
  4. import re
  5. import sys
  6. import traceback
  7. from contextlib import contextmanager
  8. from cryptography.hazmat.backends import default_backend
  9. from cryptography.hazmat.primitives import serialization as crypt_serialization
  10. from cryptography.hazmat.primitives.asymmetric import rsa
  11. from deprecation import deprecated
  12. import six
  13. import cloudbridge
  14. from ..interfaces.exceptions import InvalidParamException
  15. def generate_key_pair():
  16. """
  17. This method generates a keypair and returns it as a tuple
  18. of (public, private) keys.
  19. The public key format is OpenSSH and private key format is PEM.
  20. """
  21. key_pair = rsa.generate_private_key(
  22. backend=default_backend(),
  23. public_exponent=65537,
  24. key_size=2048)
  25. private_key = key_pair.private_bytes(
  26. crypt_serialization.Encoding.PEM,
  27. crypt_serialization.PrivateFormat.PKCS8,
  28. crypt_serialization.NoEncryption()).decode('utf-8')
  29. public_key = key_pair.public_key().public_bytes(
  30. crypt_serialization.Encoding.OpenSSH,
  31. crypt_serialization.PublicFormat.OpenSSH).decode('utf-8')
  32. return public_key, private_key
  33. def filter_by(prop_name, kwargs, objs):
  34. """
  35. Utility method for filtering a list of objects by a property.
  36. If the given property has a non empty value in kwargs, then
  37. the list of objs is filtered by that value. Otherwise, the
  38. list of objs is returned as is.
  39. """
  40. prop_val = kwargs.pop(prop_name, None)
  41. if prop_val:
  42. if isinstance(prop_val, six.string_types):
  43. regex = fnmatch.translate(prop_val)
  44. results = [o for o in objs
  45. if getattr(o, prop_name)
  46. and re.search(regex, getattr(o, prop_name))]
  47. else:
  48. results = [o for o in objs
  49. if getattr(o, prop_name) == prop_val]
  50. return results
  51. else:
  52. return objs
  53. def generic_find(filter_names, kwargs, objs):
  54. """
  55. Utility method for filtering a list of objects by a list of filters.
  56. """
  57. matches = objs
  58. for name in filter_names:
  59. matches = filter_by(name, kwargs, matches)
  60. # All kwargs should have been popped at this time.
  61. if len(kwargs) > 0:
  62. raise InvalidParamException(
  63. "Unrecognised parameters for search: %s. Supported attributes: %s"
  64. % (kwargs, filter_names))
  65. return matches
  66. @contextmanager
  67. def cleanup_action(cleanup_func):
  68. """
  69. Context manager to carry out a given
  70. cleanup action after carrying out a set
  71. of tasks, or when an exception occurs.
  72. If any errors occur during the cleanup
  73. action, those are ignored, and the original
  74. traceback is preserved.
  75. :params func: This function is called if
  76. an exception occurs or at the end of the
  77. context block. If any exceptions raised
  78. by func are ignored.
  79. Usage:
  80. with cleanup_action(lambda e: print("Oops!")):
  81. do_something()
  82. """
  83. try:
  84. yield
  85. except Exception:
  86. ex_class, ex_val, ex_traceback = sys.exc_info()
  87. try:
  88. cleanup_func()
  89. except Exception as e:
  90. print("Error during exception cleanup: {0}".format(e))
  91. traceback.print_exc()
  92. six.reraise(ex_class, ex_val, ex_traceback)
  93. try:
  94. cleanup_func()
  95. except Exception as e:
  96. print("Error during cleanup: {0}".format(e))
  97. traceback.print_exc()
  98. def get_env(varname, default_value=None):
  99. """
  100. Return the value of the environment variable or default_value.
  101. This is a helper method that wraps ``os.environ.get`` to ensure type
  102. compatibility across py2 and py3. For py2, any value obtained from an
  103. environment variable, ensure ``unicode`` type and ``str`` for py3. The
  104. casting is done only for string variables.
  105. :type varname: ``str``
  106. :param varname: Name of the environment variable for which to check.
  107. :param default_value: Return this value is the env var is not found.
  108. Defaults to ``None``.
  109. :return: Value of the supplied environment if found; value of
  110. ``default_value`` otherwise.
  111. """
  112. value = os.environ.get(varname, default_value)
  113. if isinstance(value, six.string_types) and not isinstance(
  114. value, six.text_type):
  115. return six.u(value)
  116. return value
  117. # Alias deprecation decorator, following:
  118. # https://stackoverflow.com/questions/49802412/
  119. # how-to-implement-deprecation-in-python-with-argument-alias
  120. def deprecated_alias(**aliases):
  121. def deco(f):
  122. @functools.wraps(f)
  123. def wrapper(*args, **kwargs):
  124. rename_kwargs(f.__name__, kwargs, aliases)
  125. return f(*args, **kwargs)
  126. return wrapper
  127. return deco
  128. def rename_kwargs(func_name, kwargs, aliases):
  129. for alias, new in aliases.items():
  130. if alias in kwargs:
  131. if new in kwargs:
  132. raise InvalidParamException(
  133. '{} received both {} and {}'.format(func_name, alias, new))
  134. # Manually invoke the deprecated decorator with an empty lambda
  135. # to signal deprecation
  136. deprecated(deprecated_in='1.1',
  137. removed_in='2.0',
  138. current_version=cloudbridge.__version__,
  139. details='{} is deprecated, use {} instead'.format(
  140. alias, new))(lambda: None)()
  141. kwargs[new] = kwargs.pop(alias)