PageRenderTime 27ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/silverlining/config.py

https://bitbucket.org/ianb/silverlining/
Python | 231 lines | 217 code | 9 blank | 5 comment | 22 complexity | acd077e5ee87bcffc0e1d1a420912c69 MD5 | raw file
Possible License(s): GPL-2.0
  1. import sys
  2. import os
  3. from initools.configparser import ConfigParser
  4. from UserDict import UserDict
  5. import re
  6. import fnmatch
  7. import time
  8. import cPickle as pickle
  9. from cmdutils import CommandError
  10. from libcloud.types import Provider
  11. from libcloud.providers import get_driver as libcloud_get_driver
  12. from silverlining import createconf
  13. from silversupport.env import local_location
  14. from silversupport.appdata import normalize_location
  15. class Config(UserDict):
  16. """Represents the configuration, command-line arguments, and
  17. provider.
  18. """
  19. def __init__(self, config_dict, args=None):
  20. self.data = config_dict
  21. self._driver = None
  22. self.args = args
  23. if args:
  24. self.logger = args.logger
  25. @classmethod
  26. def from_config_file(cls, filename, section, args):
  27. """Instantiate from a configuration file"""
  28. parser = ConfigParser()
  29. parser.read([filename])
  30. full_config = parser.asdict()
  31. if section not in full_config:
  32. args.logger.fatal('No section [%s]' % section)
  33. args.logger.fatal('Available sections in %s:' % filename)
  34. for name in full_config:
  35. if name.startswith('provider:'):
  36. args.logger.fatal(' [%s] (--provider=%s)'
  37. % (name, name[len('provider:'):]))
  38. raise CommandError("Bad --provider=%s"
  39. % section[len('provider:'):])
  40. config = full_config[section]
  41. config['section_name'] = section
  42. return cls(config, args=args)
  43. @property
  44. def driver(self):
  45. if self._driver is None:
  46. provider = self['provider']
  47. if ':' in provider:
  48. # Then it's a module import path
  49. mod, obj_name = provider.split(':', 1)
  50. __import__(mod)
  51. mod = sys.modules[mod]
  52. DriverClass = getattr(mod, obj_name)
  53. else:
  54. DriverClass = libcloud_get_driver(getattr(Provider, provider.upper()))
  55. self._driver = DriverClass(self['username'], self['secret'])
  56. if getattr(self.args, 'debug_libcloud', False):
  57. print 'XXX', self.args.debug_libcloud, self._driver.connection.conn_classes
  58. from libcloud.base import LoggingHTTPConnection, LoggingHTTPSConnection
  59. fp = open(self.args.debug_libcloud, 'a')
  60. LoggingHTTPConnection.log = LoggingHTTPSConnection.log = fp
  61. self._driver.connection.conn_classes = (
  62. LoggingHTTPConnection, LoggingHTTPSConnection)
  63. return self._driver
  64. @property
  65. def node_hostname(self):
  66. if getattr(self.args, 'node', None):
  67. return self.args.node
  68. if getattr(self.args, 'location'):
  69. return normalize_location(self.args.location)[0]
  70. raise CommandError(
  71. "You must give a --node option")
  72. def select_image(self, image_match=None, image_id=None, images=None):
  73. if images is None:
  74. images = self.cached_images()
  75. if image_id:
  76. image_match = 'id %s' % image_id
  77. if not image_match:
  78. if self.get('image_id'):
  79. image_match = 'id %s' % self['image_id']
  80. elif self.get('image_name'):
  81. image_match = 'name %s' % self['image_name']
  82. elif self.get('image'):
  83. image_match = self['image']
  84. return self._match('image', image_match, images)
  85. def select_size(self, size_match=None, size_id=None, sizes=None):
  86. if sizes is None:
  87. sizes = self.cached_sizes()
  88. if size_id:
  89. size_match = 'id %s' % size_id
  90. if not size_match:
  91. if self.get('size_id'):
  92. size_match = 'id %s' % self['size_id']
  93. elif self.get('size'):
  94. size_match = self['size']
  95. return self._match('size', size_match, sizes)
  96. def _match(self, type, matcher, items):
  97. matcher = matcher or ''
  98. match_pattern = matcher
  99. if ' ' in matcher:
  100. match_type, matcher = matcher.split(None, 1)
  101. else:
  102. match_type = 'name'
  103. select_first = False
  104. if matcher.split()[0] == 'first':
  105. select_first = True
  106. matcher = matcher.split(None, 1)[1]
  107. if not matcher:
  108. raise LookupError("No matcher available")
  109. possible = []
  110. for item in items:
  111. if match_type == 'id':
  112. if item.id == matcher:
  113. possible.append(item)
  114. if match_type == 'name':
  115. if '*' in matcher:
  116. if re.match(fnmatch.translate(matcher), item.name, re.I):
  117. possible.append(item)
  118. else:
  119. if item.name.lower() == matcher.lower():
  120. possible.append(item)
  121. if match_type == 'ram':
  122. if self._softint(matcher) == self._softint(item.ram):
  123. possible.append(item)
  124. if not possible:
  125. raise LookupError(
  126. "Could not find any %s that matches the pattern %s"
  127. % (type, match_pattern))
  128. if select_first:
  129. if match_type == 'name':
  130. possible.sort(key=lambda x: x.name.lower())
  131. elif match_type == 'id':
  132. possible.sort(key=lambda x: int(x.id))
  133. elif match_type == 'ram':
  134. possible.sort(key=lambda x: self._softint(x.ram))
  135. if not select_first and len(possible) > 1:
  136. raise LookupError(
  137. "Multiple %ss matched the pattern %s: %s"
  138. % (type, match_pattern, ', '.join(repr(i) for i in possible)))
  139. return possible[0]
  140. @staticmethod
  141. def _softint(v):
  142. if isinstance(v, int):
  143. return v
  144. v = re.sub(r'[^\d]', '', v)
  145. try:
  146. return int(v)
  147. except ValueError:
  148. return None
  149. def ask(self, query):
  150. if getattr(self.args, 'yes', False):
  151. self.logger.warn(
  152. "%s YES [auto]" % query)
  153. return True
  154. while 1:
  155. response = raw_input(query+" [y/n] ")
  156. response = response.strip().lower()
  157. if not response:
  158. continue
  159. if 'all' in response and response[0] == 'y':
  160. self.args.yes = True
  161. return True
  162. if response[0] == 'y':
  163. return True
  164. if response[0] == 'n':
  165. return False
  166. print 'I did not understand the response: %s' % response
  167. def cache_object(self, name, expiration=None, driver=None):
  168. path = local_location(name)
  169. if os.path.exists(path):
  170. if expiration is not None:
  171. age = time.time() - os.path.getmtime(path)
  172. if age > expiration:
  173. return None
  174. fp = open(path, 'rb')
  175. obj = pickle.load(fp)
  176. fp.close()
  177. if driver:
  178. for item in obj:
  179. item.driver = driver
  180. return obj
  181. return None
  182. def set_cache_object(self, name, obj):
  183. ## FIXME: some objects are pickleable :(
  184. path = local_location(name)
  185. saved = {}
  186. if isinstance(obj, list):
  187. for item in obj:
  188. if getattr(item, 'driver', None):
  189. saved[item] = item.driver
  190. item.driver = None
  191. try:
  192. try:
  193. fp = open(path, 'wb')
  194. pickle.dump(obj, fp)
  195. fp.close()
  196. except:
  197. if os.path.exists(path):
  198. os.unlink(path)
  199. raise
  200. finally:
  201. for item, driver in saved.iteritems():
  202. item.driver = driver
  203. def cached_images(self, expiration=None):
  204. name = self['provider'] + '-images.cache'
  205. obj = self.cache_object(name, expiration, driver=self.driver)
  206. if obj is None:
  207. obj = self.driver.list_images()
  208. self.set_cache_object(name, obj)
  209. return obj
  210. def cached_sizes(self, expiration=None):
  211. name = self['provider'] + '-sizes.cache'
  212. obj = self.cache_object(name, expiration, driver=self.driver)
  213. if obj is None:
  214. obj = self.driver.list_sizes()
  215. self.set_cache_object(name, obj)
  216. return obj