No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

datasource_plugin.py 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. from .plugins import Plugin
  2. from .exceptions import *
  3. from lodel.settings.validator import SettingValidator
  4. ##@brief Designed to handles datasources plugins
  5. #
  6. #A datasource provide data access to LeAPI typically a connector on a DB
  7. #or an API
  8. #@note For the moment implementation is done with a retro-compatibilities
  9. #priority and not with a convenience priority.
  10. #@todo Refactor and rewrite lodel2 datasource handling
  11. class DatasourcePlugin(Plugin):
  12. ##@brief Stores confspecs indicating where DatasourcePlugin list is stored
  13. _plist_confspecs = {
  14. 'section': 'lodel2',
  15. 'key': 'datasource_connectors',
  16. 'default': None,
  17. 'validator': SettingValidator('strip', none_is_valid = False) }
  18. _type_conf_name = 'datasource'
  19. def __init__(self, name):
  20. super().__init__(name)
  21. self.__datasource_cls = None
  22. def datasource_cls(self):
  23. if self.__datasource_cls is None:
  24. self.__datasource_cls = self.loader_module().Datasource
  25. return self.__datasource_cls
  26. def migration_handler_cls(self):
  27. return self.loader_module().migration_handler_class()
  28. ##@brief Return an initialized Datasource instance
  29. #@param ds_name str : The name of the datasource to instanciate
  30. #@param ro bool
  31. #@return A properly initialized Datasource instance
  32. #@throw SettingsError if an error occurs in settings
  33. #@throw DatasourcePluginError for various errors
  34. @classmethod
  35. def init_datasource(cls, ds_name, ro):
  36. plugin_name, ds_identifier = cls.plugin_name(ds_name, ro)
  37. ds_conf = cls._get_ds_connection_conf(ds_identifier, plugin_name)
  38. ds_cls = cls.get_datasource(plugin_name)
  39. return ds_cls(**ds_conf)
  40. ##@brief Return an initialized MigrationHandler instance
  41. #@param ds_name str : The datasource name
  42. #@return A properly initialized MigrationHandler instance
  43. @classmethod
  44. def init_migration_handler(cls, ds_name):
  45. plugin_name, ds_identifier = cls.plugin_name(ds_name, False)
  46. ds_conf = cls._get_ds_connection_conf(ds_identifier, plugin_name)
  47. mh_cls = cls.get_migration_handler(plugin_name)
  48. if 'read_only' in ds_conf:
  49. if ds_conf['read_only']:
  50. raise PluginError("A read only datasource was given to \
  51. migration handler !!!")
  52. del(ds_conf['read_only'])
  53. return mh_cls(**ds_conf)
  54. ##@brief Given a datasource name returns a DatasourcePlugin name
  55. #@param ds_name str : datasource name
  56. #@param ro bool : if true consider the datasource as readonly
  57. #@return a DatasourcePlugin name
  58. #@throw PluginError if datasource name not found
  59. #@throw DatasourcePermError if datasource is read_only but ro flag arg is
  60. #false
  61. @staticmethod
  62. def plugin_name(ds_name, ro):
  63. from lodel.settings import Settings
  64. # fetching connection identifier given datasource name
  65. try:
  66. ds_identifier = getattr(Settings.datasources, ds_name)
  67. except (NameError, AttributeError):
  68. raise DatasourcePluginError("Unknown or unconfigured datasource \
  69. '%s'" % ds_name)
  70. # fetching read_only flag
  71. try:
  72. read_only = getattr(ds_identifier, 'read_only')
  73. except (NameError, AttributeError):
  74. raise SettingsError("Malformed datasource configuration for '%s' \
  75. : missing read_only key" % ds_name)
  76. # fetching datasource identifier
  77. try:
  78. ds_identifier = getattr(ds_identifier, 'identifier')
  79. except (NameError,AttributeError) as e:
  80. raise SettingsError("Malformed datasource configuration for '%s' \
  81. : missing identifier key" % ds_name)
  82. # settings and ro arg consistency check
  83. if read_only and not ro:
  84. raise DatasourcePluginError("ro argument was set to False but \
  85. True found in settings for datasource '%s'" % ds_name)
  86. res = ds_identifier.split('.')
  87. if len(res) != 2:
  88. raise SettingsError("expected value for identifier is like \
  89. DS_PLUGIN_NAME.DS_INSTANCE_NAME. But got %s" % ds_identifier)
  90. return res
  91. ##@brief Try to fetch a datasource configuration
  92. #@param ds_identifier str : datasource name
  93. #@param ds_plugin_name : datasource plugin name
  94. #@return a dict containing datasource initialisation options
  95. #@throw NameError if a datasource plugin or instance cannot be found
  96. @staticmethod
  97. def _get_ds_connection_conf(ds_identifier,ds_plugin_name):
  98. from lodel.settings import Settings
  99. if ds_plugin_name not in Settings.datasource._fields:
  100. msg = "Unknown or unconfigured datasource plugin %s"
  101. msg %= ds_plugin
  102. raise DatasourcePluginError(msg)
  103. ds_conf = getattr(Settings.datasource, ds_plugin_name)
  104. if ds_identifier not in ds_conf._fields:
  105. msg = "Unknown or unconfigured datasource instance %s"
  106. msg %= ds_identifier
  107. raise DatasourcePluginError(msg)
  108. ds_conf = getattr(ds_conf, ds_identifier)
  109. return {k: getattr(ds_conf,k) for k in ds_conf._fields }
  110. ##@brief DatasourcePlugin instance accessor
  111. #@param ds_name str : plugin name
  112. #@return a DatasourcePlugin instance
  113. #@throw PluginError if no plugin named ds_name found
  114. #@throw PluginTypeError if ds_name ref to a plugin that is not a
  115. #DatasourcePlugin
  116. @classmethod
  117. def get(cls, ds_name):
  118. pinstance = super().get(ds_name) #Will raise PluginError if bad name
  119. if not isinstance(pinstance, DatasourcePlugin):
  120. raise PluginTypeErrror("A name of a DatasourcePlugin was excepted \
  121. but %s is a %s" % (ds_name, pinstance.__class__.__name__))
  122. return pinstance
  123. ##@brief Return a datasource class given a datasource name
  124. #@param ds_name str : datasource plugin name
  125. #@throw PluginError if ds_name is not an existing plugin name
  126. #@throw PluginTypeError if ds_name is not the name of a DatasourcePlugin
  127. @classmethod
  128. def get_datasource(cls, ds_plugin_name):
  129. return cls.get(ds_plugin_name).datasource_cls()
  130. @classmethod
  131. def get_migration_handler(cls, ds_plugin_name):
  132. return cls.get(ds_plugin_name).migration_handler_cls()