registry.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. #!/usr/bin/env python
  2. # Licensed to Cloudera, Inc. under one
  3. # or more contributor license agreements. See the NOTICE file
  4. # distributed with this work for additional information
  5. # regarding copyright ownership. Cloudera, Inc. licenses this file
  6. # to you under the Apache License, Version 2.0 (the
  7. # "License"); you may not use this file except in compliance
  8. # with the License. You may obtain a copy of the License at
  9. #
  10. # http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing, software
  13. # distributed under the License is distributed on an "AS IS" BASIS,
  14. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. # See the License for the specific language governing permissions and
  16. # limitations under the License.
  17. """
  18. Registry for the applications
  19. """
  20. import glob
  21. import logging
  22. import os
  23. import json
  24. import common
  25. LOG = logging.getLogger(__name__)
  26. class AppRegistry(object):
  27. """
  28. Represents a registry.
  29. """
  30. def __init__(self):
  31. """Open the existing registry"""
  32. self._reg_path = os.path.join(common.HUE_APP_REG_DIR, 'app.reg')
  33. self._initialized = False
  34. self._apps = { } # Map of name -> HueApp
  35. self._open()
  36. def _open(self):
  37. """Open the registry file. May raise OSError"""
  38. if os.path.exists(self._reg_path):
  39. reg_file = file(self._reg_path)
  40. app_list = json.load(reg_file)
  41. reg_file.close()
  42. for app_json in app_list:
  43. app_json.setdefault('author', 'Unknown') # Added after 0.9
  44. app = HueApp.create(app_json)
  45. self._apps[app.name] = app
  46. self._initialized = True
  47. def _write(self, path):
  48. """Write out the registry to the given path"""
  49. outfile = file(path, 'w')
  50. json.dump(self._apps.values(), outfile, cls=AppJsonEncoder, indent=2)
  51. outfile.close()
  52. def contains(self, app):
  53. """Returns whether the app (of the same version) is in the registry"""
  54. try:
  55. existing = self._apps[app.name]
  56. return existing.version == app.version
  57. except KeyError:
  58. return False
  59. def register(self, app):
  60. """register(app) -> True/False"""
  61. assert self._initialized, "Registry not yet initialized"
  62. try:
  63. existing = self._apps[app.name]
  64. version_diff = common.cmp_version(existing.version, app.version)
  65. if version_diff == 0:
  66. LOG.warn('%s is already registered' % (app,))
  67. return False
  68. elif version_diff < 0:
  69. LOG.info('Upgrading %s from version %s' % (app, existing.version))
  70. elif version_diff > 0:
  71. LOG.error('A newer version (%s) of %s is already installed' % (existing.version, app))
  72. return False
  73. except KeyError:
  74. pass
  75. LOG.info('Updating registry with %s' % (app,))
  76. self._apps[app.name] = app
  77. return True
  78. def unregister(self, app_name):
  79. """unregister(app_Name) -> HueApp. May raise KeyError"""
  80. assert self._initialized, "Registry not yet initialized"
  81. app = self._apps[app_name]
  82. del self._apps[app_name]
  83. return app
  84. def get_all_apps(self):
  85. """get_all_apps() -> List of HueApp"""
  86. return self._apps.values()
  87. def save(self):
  88. """Save and write out the registry"""
  89. assert self._initialized, "Registry not yet initialized"
  90. self._write(self._reg_path)
  91. LOG.info('=== Saved registry at %s' % (self._reg_path,))
  92. class HueApp(object):
  93. """
  94. Represents an app.
  95. Path provided should be absolute or relative to common.APPS_ROOT
  96. """
  97. @staticmethod
  98. def create(json):
  99. return HueApp(json['name'], json['version'], json['path'], json['desc'], json['author'])
  100. def __init__(self, name, version, path, desc, author):
  101. self.name = name
  102. self.version = version
  103. self.path = path
  104. self.desc = desc
  105. self.author = author
  106. def __str__(self):
  107. return "%s v.%s" % (self.name, self.version)
  108. def __cmp__(self, other):
  109. if not isinstance(other, HueApp):
  110. raise TypeError
  111. return cmp((self.name, self.version), (other.name, other.version))
  112. @property
  113. def rel_path(self):
  114. if os.path.isabs(self.path):
  115. return os.path.relpath(self.path, common.APPS_ROOT)
  116. else:
  117. return self.path
  118. @property
  119. def abs_path(self):
  120. if not os.path.isabs(self.path):
  121. return os.path.abspath(os.path.join(common.APPS_ROOT, self.path))
  122. else:
  123. return self.path
  124. def use_rel_path(self):
  125. self.path = self.rel_path
  126. def use_abs_path(self):
  127. self.path = self.abs_path
  128. def jsonable(self):
  129. return dict(name=self.name, version=self.version, path=self.path,
  130. desc=self.desc, author=self.author)
  131. def find_ext_pys(self):
  132. """find_ext_pys() -> A list of paths for all ext-py packages"""
  133. return glob.glob(os.path.join(self.abs_path, 'ext-py', '*'))
  134. def get_conffiles(self):
  135. """get_conffiles() -> A list of config (.ini) files"""
  136. return glob.glob(os.path.join(self.abs_path, 'conf', '*.ini'))
  137. def install_conf(self):
  138. """
  139. install_conf() -> True/False
  140. Symlink the app's conf/*.ini files into the conf directory.
  141. """
  142. installed = [ ]
  143. for target in self.get_conffiles():
  144. link_name = os.path.join(common.HUE_CONF_DIR, os.path.basename(target))
  145. # Does the link already exists?
  146. if os.path.islink(link_name):
  147. try:
  148. cur = os.readlink(link_name)
  149. if cur == target:
  150. LOG.warn("Symlink for configuration already exists: %s" % (link_name,))
  151. installed.append(link_name)
  152. continue
  153. # Remove broken link
  154. if not os.path.exists(cur):
  155. os.unlink(link_name)
  156. LOG.warn("Removing broken link: %s" % (link_name,))
  157. except OSError, ex:
  158. LOG.warn("Error checking for existing link %s: %s" % (link_name, ex))
  159. # Actually install the link
  160. try:
  161. os.symlink(target, link_name)
  162. LOG.info('Symlink config %s -> %s' % (link_name, target))
  163. installed.append(link_name)
  164. except OSError, ex:
  165. LOG.error("Failed to symlink %s to %s: %s" % (target, link_name, ex))
  166. for lnk in installed:
  167. try:
  168. os.unlink(lnk)
  169. except OSError, ex2:
  170. LOG.error("Failed to cleanup link %s: %s" % (link_name, ex2))
  171. return False
  172. return True
  173. def uninstall_conf(self):
  174. """uninstall_conf() -> True/False"""
  175. app_conf_dir = os.path.abspath(os.path.join(self.abs_path, 'conf'))
  176. if not os.path.isdir(app_conf_dir):
  177. return True
  178. # Check all symlink in the conf dir and remove any that point to this app
  179. for name in os.listdir(common.HUE_CONF_DIR):
  180. path = os.path.join(common.HUE_CONF_DIR, name)
  181. if not os.path.islink(path):
  182. continue
  183. target = os.readlink(path)
  184. target_dir = os.path.abspath(os.path.dirname(target))
  185. if target_dir == app_conf_dir:
  186. try:
  187. os.unlink(path)
  188. LOG.info('Remove config symlink %s -> %s' % (path, target))
  189. except OSError, ex:
  190. LOG.error("Failed to remove configuration link %s: %s" % (path, ex))
  191. return False
  192. return True
  193. class AppJsonEncoder(json.JSONEncoder):
  194. def __init__(self, **kwargs):
  195. json.JSONEncoder.__init__(self, **kwargs)
  196. def default(self, obj):
  197. if isinstance(obj, HueApp):
  198. return obj.jsonable()
  199. return json.JSONEncoder.default(self, obj)