registry.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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. try:
  24. import json
  25. except ImportError:
  26. import simplejson as json
  27. import common
  28. LOG = logging.getLogger(__name__)
  29. class AppRegistry(object):
  30. """
  31. Represents a registry.
  32. """
  33. def __init__(self):
  34. """Open the existing registry"""
  35. self._reg_path = os.path.join(os.environ.get("HUE_APP_REG_DIR", common.INSTALL_ROOT), 'app.reg')
  36. self._initialized = False
  37. self._apps = { } # Map of name -> HueApp
  38. self._open()
  39. def _open(self):
  40. """Open the registry file. May raise OSError"""
  41. if os.path.exists(self._reg_path):
  42. reg_file = file(self._reg_path)
  43. app_list = json.load(reg_file)
  44. reg_file.close()
  45. for app_json in app_list:
  46. app_json.setdefault('author', 'Unknown') # Added after 0.9
  47. app = HueApp.create(app_json)
  48. self._apps[app.name] = app
  49. self._initialized = True
  50. def _write(self, path):
  51. """Write out the registry to the given path"""
  52. outfile = file(path, 'w')
  53. json.dump(self._apps.values(), outfile, cls=AppJsonEncoder, indent=2)
  54. outfile.close()
  55. def contains(self, app):
  56. """Returns whether the app (of the same version) is in the registry"""
  57. try:
  58. existing = self._apps[app.name]
  59. return existing.version == app.version
  60. except KeyError:
  61. return False
  62. def register(self, app):
  63. """register(app) -> True/False"""
  64. assert self._initialized, "Registry not yet initialized"
  65. try:
  66. existing = self._apps[app.name]
  67. version_diff = common.cmp_version(existing.version, app.version)
  68. if version_diff == 0:
  69. LOG.warn('%s is already registered' % (app,))
  70. return False
  71. elif version_diff < 0:
  72. LOG.info('Upgrading %s from version %s' % (app, existing.version))
  73. elif version_diff > 0:
  74. LOG.error('A newer version (%s) of %s is already installed' % (existing.version, app))
  75. return False
  76. except KeyError:
  77. pass
  78. LOG.info('Updating registry with %s' % (app,))
  79. self._apps[app.name] = app
  80. return True
  81. def unregister(self, app_name):
  82. """unregister(app_Name) -> HueApp. May raise KeyError"""
  83. assert self._initialized, "Registry not yet initialized"
  84. app = self._apps[app_name]
  85. del self._apps[app_name]
  86. return app
  87. def get_all_apps(self):
  88. """get_all_apps() -> List of HueApp"""
  89. return self._apps.values()
  90. def save(self):
  91. """Save and write out the registry"""
  92. assert self._initialized, "Registry not yet initialized"
  93. tmp_path = self._reg_path + '.new'
  94. self._write(tmp_path)
  95. os.rename(tmp_path, self._reg_path)
  96. LOG.info('=== Saved registry at %s' % (self._reg_path,))
  97. class HueApp(object):
  98. """
  99. Represents an app.
  100. """
  101. @staticmethod
  102. def create(json):
  103. return HueApp(json['name'], json['version'], json['path'], json['desc'], json['author'])
  104. def __init__(self, name, version, path, desc, author):
  105. self.name = name
  106. self.version = version
  107. self.path = path
  108. self.desc = desc
  109. self.author = author
  110. def __str__(self):
  111. return "%s v.%s" % (self.name, self.version)
  112. def __cmp__(self, other):
  113. if not isinstance(other, HueApp):
  114. raise TypeError
  115. return cmp((self.name, self.version), (other.name, other.version))
  116. def jsonable(self):
  117. return dict(name=self.name, version=self.version, path=self.path,
  118. desc=self.desc, author=self.author)
  119. def find_ext_pys(self):
  120. """find_ext_pys() -> A list of paths for all ext-py packages"""
  121. return glob.glob(os.path.join(self.path, 'ext-py', '*'))
  122. def get_conffiles(self):
  123. """get_conffiles() -> A list of config (.ini) files"""
  124. ini_files = glob.glob(os.path.join(self.path, 'conf', '*.ini'))
  125. return [ os.path.abspath(ini) for ini in ini_files ]
  126. def install_conf(self):
  127. """
  128. install_conf() -> True/False
  129. Symlink the app's conf/*.ini files into the conf directory.
  130. """
  131. installed = [ ]
  132. for target in self.get_conffiles():
  133. link_name = os.path.join(common.HUE_CONF_DIR, os.path.basename(target))
  134. # Does the link already exists?
  135. if os.path.islink(link_name):
  136. try:
  137. cur = os.readlink(link_name)
  138. if cur == target:
  139. LOG.warn("Symlink for configuration already exists: %s" % (link_name,))
  140. installed.append(link_name)
  141. continue
  142. # Remove broken link
  143. if not os.path.exists(cur):
  144. os.unlink(link_name)
  145. LOG.warn("Removing broken link: %s" % (link_name,))
  146. except OSError, ex:
  147. LOG.warn("Error checking for existing link %s: %s" % (link_name, ex))
  148. # Actually install the link
  149. try:
  150. os.symlink(target, link_name)
  151. LOG.info('Symlink config %s -> %s' % (link_name, target))
  152. installed.append(link_name)
  153. except OSError, ex:
  154. LOG.error("Failed to symlink %s to %s: %s" % (target, link_name, ex))
  155. for lnk in installed:
  156. try:
  157. os.unlink(lnk)
  158. except OSError, ex2:
  159. LOG.error("Failed to cleanup link %s: %s" % (link_name, ex2))
  160. return False
  161. return True
  162. def uninstall_conf(self):
  163. """uninstall_conf() -> True/False"""
  164. app_conf_dir = os.path.abspath(os.path.join(self.path, 'conf'))
  165. if not os.path.isdir(app_conf_dir):
  166. return True
  167. # Check all symlink in the conf dir and remove any that point to this app
  168. for name in os.listdir(common.HUE_CONF_DIR):
  169. path = os.path.join(common.HUE_CONF_DIR, name)
  170. if not os.path.islink(path):
  171. continue
  172. target = os.readlink(path)
  173. target_dir = os.path.abspath(os.path.dirname(target))
  174. if target_dir == app_conf_dir:
  175. try:
  176. os.unlink(path)
  177. LOG.info('Remove config symlink %s -> %s' % (path, target))
  178. except OSError, ex:
  179. LOG.error("Failed to remove configuration link %s: %s" % (path, ex))
  180. return False
  181. return True
  182. class AppJsonEncoder(json.JSONEncoder):
  183. def __init__(self, **kwargs):
  184. json.JSONEncoder.__init__(self, **kwargs)
  185. def default(self, obj):
  186. if isinstance(obj, HueApp):
  187. return obj.jsonable()
  188. return json.JSONEncoder.default(self, obj)