goe-framework 1.0.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. goe/__init__.py +17 -0
  2. goe/conductor/__init__.py +0 -0
  3. goe/conductor/hybrid_view_service.py +334 -0
  4. goe/config/__init__.py +0 -0
  5. goe/config/config_file.py +53 -0
  6. goe/config/config_validation_functions.py +663 -0
  7. goe/config/option_descriptions.py +38 -0
  8. goe/config/orchestration_config.py +996 -0
  9. goe/config/orchestration_defaults.py +999 -0
  10. goe/connect/__init__.py +0 -0
  11. goe/connect/connect.py +555 -0
  12. goe/connect/connect_backend.py +481 -0
  13. goe/connect/connect_constants.py +23 -0
  14. goe/connect/connect_frontend.py +265 -0
  15. goe/connect/connect_functions.py +112 -0
  16. goe/connect/connect_transport.py +336 -0
  17. goe/data_governance/__init__.py +0 -0
  18. goe/data_governance/cloudera_navigator.py +845 -0
  19. goe/data_governance/hadoop_data_governance.py +616 -0
  20. goe/data_governance/hadoop_data_governance_constants.py +90 -0
  21. goe/data_governance/hadoop_data_governance_interface.py +209 -0
  22. goe/exceptions.py +25 -0
  23. goe/filesystem/__init__.py +0 -0
  24. goe/filesystem/cli_hdfs.py +539 -0
  25. goe/filesystem/goe_azure.py +483 -0
  26. goe/filesystem/goe_dfs.py +424 -0
  27. goe/filesystem/goe_dfs_factory.py +110 -0
  28. goe/filesystem/goe_gcs.py +352 -0
  29. goe/filesystem/goe_s3.py +363 -0
  30. goe/filesystem/web_hdfs.py +507 -0
  31. goe/goe.py +3526 -0
  32. goe/listener/__init__.py +0 -0
  33. goe/listener/__main__.py +36 -0
  34. goe/listener/api/__init__.py +0 -0
  35. goe/listener/api/routes/__init__.py +0 -0
  36. goe/listener/api/routes/docs.py +68 -0
  37. goe/listener/api/routes/orchestration.py +185 -0
  38. goe/listener/api/routes/system.py +186 -0
  39. goe/listener/asgi.py +120 -0
  40. goe/listener/config/__init__.py +10 -0
  41. goe/listener/config/application.py +113 -0
  42. goe/listener/config/gunicorn.conf.py +284 -0
  43. goe/listener/config/logging.py +81 -0
  44. goe/listener/config/router.py +44 -0
  45. goe/listener/core/__init__.py +0 -0
  46. goe/listener/core/enum.py +24 -0
  47. goe/listener/core/events.py +44 -0
  48. goe/listener/core/middleware/__init__.py +6 -0
  49. goe/listener/core/middleware/compression.py +297 -0
  50. goe/listener/core/middleware/cors.py +38 -0
  51. goe/listener/core/middleware/secure_headers.py +37 -0
  52. goe/listener/core/security.py +46 -0
  53. goe/listener/core/worker.py +149 -0
  54. goe/listener/exceptions/__init__.py +38 -0
  55. goe/listener/exceptions/base.py +279 -0
  56. goe/listener/exceptions/errors.py +99 -0
  57. goe/listener/heartbeat.py +47 -0
  58. goe/listener/prestart.py +85 -0
  59. goe/listener/schemas/__init__.py +58 -0
  60. goe/listener/schemas/base.py +102 -0
  61. goe/listener/schemas/error.py +133 -0
  62. goe/listener/schemas/generics.py +40 -0
  63. goe/listener/schemas/orchestration.py +896 -0
  64. goe/listener/schemas/system.py +191 -0
  65. goe/listener/services/__init__.py +8 -0
  66. goe/listener/services/heartbeat.py +115 -0
  67. goe/listener/services/hybrid_view.py +356 -0
  68. goe/listener/services/orchestrate.py +24 -0
  69. goe/listener/services/periodic_tasks.py +196 -0
  70. goe/listener/services/system.py +126 -0
  71. goe/listener/utils/__init__.py +10 -0
  72. goe/listener/utils/cache.py +444 -0
  73. goe/listener/utils/group_by.py +26 -0
  74. goe/listener/utils/orchestrate.py +52 -0
  75. goe/listener/utils/ping.py +47 -0
  76. goe/listener/utils/system.py +83 -0
  77. goe/listener/worker.py +67 -0
  78. goe/listener/wsgi.py +130 -0
  79. goe/offload/__init__.py +0 -0
  80. goe/offload/backend_api.py +2081 -0
  81. goe/offload/backend_table.py +2645 -0
  82. goe/offload/bigquery/__init__.py +0 -0
  83. goe/offload/bigquery/bigquery_backend_api.py +2579 -0
  84. goe/offload/bigquery/bigquery_backend_table.py +664 -0
  85. goe/offload/bigquery/bigquery_column.py +167 -0
  86. goe/offload/bigquery/bigquery_literal.py +137 -0
  87. goe/offload/bigquery/bigquery_predicate.py +68 -0
  88. goe/offload/column_metadata.py +672 -0
  89. goe/offload/factory/__init__.py +0 -0
  90. goe/offload/factory/backend_api_factory.py +130 -0
  91. goe/offload/factory/backend_table_factory.py +162 -0
  92. goe/offload/factory/frontend_api_factory.py +96 -0
  93. goe/offload/factory/offload_source_table_factory.py +92 -0
  94. goe/offload/factory/offload_transport_factory.py +237 -0
  95. goe/offload/factory/offload_transport_rdbms_api_factory.py +64 -0
  96. goe/offload/factory/query_import_factory.py +45 -0
  97. goe/offload/factory/staging_file_factory.py +92 -0
  98. goe/offload/format_literal.py +70 -0
  99. goe/offload/frontend_api.py +882 -0
  100. goe/offload/hadoop/__init__.py +0 -0
  101. goe/offload/hadoop/hadoop_backend_api.py +1961 -0
  102. goe/offload/hadoop/hadoop_backend_table.py +898 -0
  103. goe/offload/hadoop/hadoop_column.py +151 -0
  104. goe/offload/hadoop/hadoop_predicate.py +82 -0
  105. goe/offload/hadoop/hive_backend_api.py +755 -0
  106. goe/offload/hadoop/hive_backend_table.py +257 -0
  107. goe/offload/hadoop/hive_literal.py +91 -0
  108. goe/offload/hadoop/impala_backend_api.py +1215 -0
  109. goe/offload/hadoop/impala_backend_table.py +273 -0
  110. goe/offload/hadoop/impala_literal.py +68 -0
  111. goe/offload/hadoop/sqoop_offload_transport.py +612 -0
  112. goe/offload/microsoft/__init__.py +0 -0
  113. goe/offload/microsoft/mssql_column.py +228 -0
  114. goe/offload/microsoft/mssql_frontend_api.py +650 -0
  115. goe/offload/microsoft/mssql_offload_source_table.py +735 -0
  116. goe/offload/microsoft/mssql_offload_transport_rdbms_api.py +204 -0
  117. goe/offload/microsoft/synapse_backend_api.py +2890 -0
  118. goe/offload/microsoft/synapse_backend_table.py +663 -0
  119. goe/offload/microsoft/synapse_column.py +220 -0
  120. goe/offload/microsoft/synapse_constants.py +29 -0
  121. goe/offload/microsoft/synapse_literal.py +156 -0
  122. goe/offload/microsoft/synapse_predicate.py +71 -0
  123. goe/offload/offload.py +706 -0
  124. goe/offload/offload_constants.py +605 -0
  125. goe/offload/offload_functions.py +588 -0
  126. goe/offload/offload_messages.py +708 -0
  127. goe/offload/offload_metadata_functions.py +462 -0
  128. goe/offload/offload_source_data.py +3257 -0
  129. goe/offload/offload_source_table.py +1295 -0
  130. goe/offload/offload_status_report.py +4038 -0
  131. goe/offload/offload_transport.py +2873 -0
  132. goe/offload/offload_transport_functions.py +466 -0
  133. goe/offload/offload_transport_rdbms_api.py +289 -0
  134. goe/offload/offload_validation.py +1052 -0
  135. goe/offload/offload_xform_functions.py +100 -0
  136. goe/offload/operation/__init__.py +0 -0
  137. goe/offload/operation/data_type_controls.py +365 -0
  138. goe/offload/operation/ddl_file.py +125 -0
  139. goe/offload/operation/not_null_columns.py +107 -0
  140. goe/offload/operation/partition_controls.py +521 -0
  141. goe/offload/operation/sort_columns.py +152 -0
  142. goe/offload/operation/stats_controls.py +425 -0
  143. goe/offload/operation/table_structure_checks.py +181 -0
  144. goe/offload/operation/transport.py +167 -0
  145. goe/offload/option_validation.py +217 -0
  146. goe/offload/oracle/__init__.py +0 -0
  147. goe/offload/oracle/oracle_column.py +326 -0
  148. goe/offload/oracle/oracle_frontend_api.py +1200 -0
  149. goe/offload/oracle/oracle_literal.py +129 -0
  150. goe/offload/oracle/oracle_offload_source_table.py +1765 -0
  151. goe/offload/oracle/oracle_offload_transport_rdbms_api.py +1106 -0
  152. goe/offload/oracle/oracle_predicate.py +129 -0
  153. goe/offload/predicate_offload.py +742 -0
  154. goe/offload/query_import_interface.py +92 -0
  155. goe/offload/snowflake/__init__.py +0 -0
  156. goe/offload/snowflake/snowflake_backend_api.py +2326 -0
  157. goe/offload/snowflake/snowflake_backend_table.py +747 -0
  158. goe/offload/snowflake/snowflake_column.py +161 -0
  159. goe/offload/snowflake/snowflake_literal.py +124 -0
  160. goe/offload/snowflake/snowflake_predicate.py +67 -0
  161. goe/offload/spark/__init__.py +0 -0
  162. goe/offload/spark/dataproc_offload_transport.py +615 -0
  163. goe/offload/spark/livy_offload_transport.py +551 -0
  164. goe/offload/spark/offload_transport_livy_requests.py +113 -0
  165. goe/offload/spark/pyspark_literal.py +69 -0
  166. goe/offload/spark/spark_thrift_backend_api.py +257 -0
  167. goe/offload/staging/__init__.py +0 -0
  168. goe/offload/staging/avro/__init__.py +0 -0
  169. goe/offload/staging/avro/avro_column.py +108 -0
  170. goe/offload/staging/avro/avro_staging_file.py +416 -0
  171. goe/offload/staging/parquet/__init__.py +0 -0
  172. goe/offload/staging/parquet/parquet_column.py +113 -0
  173. goe/offload/staging/parquet/parquet_staging_file.py +311 -0
  174. goe/offload/staging/staging_file.py +155 -0
  175. goe/offload/synthetic_partition_literal.py +249 -0
  176. goe/offload/teradata/__init__.py +0 -0
  177. goe/offload/teradata/teradata_column.py +309 -0
  178. goe/offload/teradata/teradata_frontend_api.py +908 -0
  179. goe/offload/teradata/teradata_literal.py +162 -0
  180. goe/offload/teradata/teradata_offload_source_table.py +1192 -0
  181. goe/offload/teradata/teradata_offload_transport_rdbms_api.py +620 -0
  182. goe/offload/teradata/teradata_partition_expression.py +205 -0
  183. goe/offload/teradata/teradata_predicate.py +86 -0
  184. goe/orchestration/__init__.py +0 -0
  185. goe/orchestration/cli_entry_points.py +79 -0
  186. goe/orchestration/command_steps.py +126 -0
  187. goe/orchestration/execution_id.py +101 -0
  188. goe/orchestration/orchestration_constants.py +56 -0
  189. goe/orchestration/orchestration_lock.py +164 -0
  190. goe/orchestration/orchestration_runner.py +539 -0
  191. goe/persistence/__init__.py +0 -0
  192. goe/persistence/factory/__init__.py +0 -0
  193. goe/persistence/factory/orchestration_repo_client_factory.py +48 -0
  194. goe/persistence/oracle/__init__.py +0 -0
  195. goe/persistence/oracle/oracle_orchestration_repo_client.py +1093 -0
  196. goe/persistence/orchestration_metadata.py +449 -0
  197. goe/persistence/orchestration_repo_client.py +436 -0
  198. goe/persistence/teradata/__init__.py +0 -0
  199. goe/persistence/teradata/teradata_orchestration_repo_client.py +358 -0
  200. goe/schema_sync/__init__.py +0 -0
  201. goe/schema_sync/schema_sync.py +170 -0
  202. goe/schema_sync/schema_sync_analyzer.py +663 -0
  203. goe/schema_sync/schema_sync_command_file.py +94 -0
  204. goe/schema_sync/schema_sync_constants.py +38 -0
  205. goe/schema_sync/schema_sync_processor.py +109 -0
  206. goe/schema_sync/schema_sync_step.py +91 -0
  207. goe/schema_sync/steps/__init__.py +0 -0
  208. goe/schema_sync/steps/add_backend_column.py +107 -0
  209. goe/schema_sync/steps/add_oracle_column.py +101 -0
  210. goe/schema_sync/steps/step_builder.py +30 -0
  211. goe/scripts/__init__.py +0 -0
  212. goe/scripts/agg_validate.py +288 -0
  213. goe/util/__init__.py +0 -0
  214. goe/util/avro_encoder.py +258 -0
  215. goe/util/better_impyla.py +1748 -0
  216. goe/util/exception_trigger.py +52 -0
  217. goe/util/goe_log.py +152 -0
  218. goe/util/goe_log_fh.py +67 -0
  219. goe/util/goe_version.py +18 -0
  220. goe/util/hive_ddl_transform.py +396 -0
  221. goe/util/hive_table_stats.py +1590 -0
  222. goe/util/hs2_connection.py +310 -0
  223. goe/util/json_tools.py +60 -0
  224. goe/util/local_cache.py +312 -0
  225. goe/util/misc_functions.py +775 -0
  226. goe/util/ora_query.py +417 -0
  227. goe/util/parallel_exec.py +222 -0
  228. goe/util/parquet_encoder.py +206 -0
  229. goe/util/password_tools.py +261 -0
  230. goe/util/polling_thread.py +97 -0
  231. goe/util/redis_tools.py +443 -0
  232. goe/util/retry_logic.py +165 -0
  233. goe/util/simple_timer.py +49 -0
  234. goe_framework-1.0.6.dist-info/AUTHORS +10 -0
  235. goe_framework-1.0.6.dist-info/LICENSE +201 -0
  236. goe_framework-1.0.6.dist-info/METADATA +210 -0
  237. goe_framework-1.0.6.dist-info/RECORD +239 -0
  238. goe_framework-1.0.6.dist-info/WHEEL +5 -0
  239. goe_framework-1.0.6.dist-info/top_level.txt +1 -0
@@ -0,0 +1,663 @@
1
+ #! /usr/bin/env python3
2
+
3
+ # Copyright 2016 The GOE Authors. All rights reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ """ config_validation_functions: Library of functions used in goe.py and OrchestrationConfig
18
+ """
19
+
20
+ import json
21
+ import random
22
+ import re
23
+
24
+ from goe.config import orchestration_defaults
25
+ from goe.data_governance.hadoop_data_governance_constants import (
26
+ SUPPORTED_DATA_GOVERNANCE_BACKENDS,
27
+ )
28
+ from goe.data_governance.hadoop_data_governance import (
29
+ is_valid_data_governance_tag,
30
+ is_valid_data_governance_auto_property,
31
+ )
32
+ from goe.filesystem.goe_dfs import (
33
+ AZURE_OFFLOAD_FS_SCHEMES,
34
+ OFFLOAD_FS_SCHEME_MAPRFS,
35
+ VALID_OFFLOAD_FS_SCHEMES,
36
+ OFFLOAD_FS_SCHEMES_REQUIRING_CONTAINER,
37
+ )
38
+ from goe.offload.microsoft.synapse_constants import (
39
+ SYNAPSE_AUTH_MECHANISM_AD_SERVICE_PRINCIPAL,
40
+ SYNAPSE_VALID_AUTH_MECHANISMS,
41
+ SYNAPSE_USER_PASS_AUTH_MECHANISMS,
42
+ )
43
+ from goe.offload.offload_constants import (
44
+ BACKEND_DISTRO_GCP,
45
+ BACKEND_DISTRO_MAPR,
46
+ BACKEND_DISTRO_SNOWFLAKE,
47
+ BACKEND_DISTRO_MSAZURE,
48
+ DBTYPE_HIVE,
49
+ DBTYPE_IMPALA,
50
+ DBTYPE_MSSQL,
51
+ DBTYPE_ORACLE,
52
+ DBTYPE_TERADATA,
53
+ HADOOP_BASED_BACKEND_DISTRIBUTIONS,
54
+ )
55
+ from goe.offload.offload_transport import (
56
+ derive_rest_api_verify_value_from_url,
57
+ SPARK_SUBMIT_SPARK_YARN_MASTER,
58
+ VALID_OFFLOAD_TRANSPORTS,
59
+ VALID_SPARK_SUBMIT_EXECUTABLES,
60
+ )
61
+ from goe.util.goe_log_fh import is_valid_path_for_logs
62
+ from goe.util.misc_functions import human_size_to_bytes
63
+ from goe.util.password_tools import PasswordTools
64
+
65
+
66
+ class OrchestrationConfigException(Exception):
67
+ pass
68
+
69
+
70
+ ###########################################################################
71
+ # GLOBAL FUNCTIONS
72
+ ###########################################################################
73
+
74
+
75
+ def verify_json_option(option_name, option_value):
76
+ if option_value:
77
+ try:
78
+ properties = json.loads(option_value)
79
+
80
+ invalid_props = [
81
+ k for k, v in properties.items() if type(v) not in (str, int, float)
82
+ ]
83
+ if invalid_props:
84
+ prop_details = "\n".join(
85
+ f"Invalid property value for key/value pair: {k}: {properties[k]}"
86
+ for k in invalid_props
87
+ )
88
+ raise OrchestrationConfigException(
89
+ "Invalid property value in {} for keys: {}\n{}".format(
90
+ option_name, str(invalid_props), prop_details
91
+ )
92
+ )
93
+ except ValueError as ve:
94
+ raise OrchestrationConfigException(
95
+ "Invalid JSON value for %s: %s" % (option_name, str(ve))
96
+ ) from ve
97
+
98
+
99
+ ###########################################################################
100
+ # NORMALISATION FUNCTIONS
101
+ # For the time being these are a lift and shift from goe.py while we
102
+ # migrate to OrchestrationConfig.
103
+ ###########################################################################
104
+
105
+
106
+ def normalise_backend_options(options):
107
+ normalise_bigquery_options(options)
108
+ normalise_hadoop_options(options)
109
+ normalise_snowflake_options(options)
110
+ normalise_synapse_options(options)
111
+
112
+ if options.backend_identifier_case:
113
+ options.backend_identifier_case = options.backend_identifier_case.upper()
114
+ else:
115
+ options.backend_identifier_case = (
116
+ orchestration_defaults.backend_identifier_case_default()
117
+ )
118
+ if options.backend_identifier_case not in ["UPPER", "LOWER", "NO_MODIFY"]:
119
+ raise OrchestrationConfigException(
120
+ "Invalid value for BACKEND_IDENTIFIER_CASE, valid values: UPPER|LOWER|NO_MODIFY"
121
+ )
122
+
123
+
124
+ def normalise_db_prefix_and_paths(options, frontend_api=None):
125
+ if options.db_name_prefix is None:
126
+ assert frontend_api
127
+ options.db_name_prefix = frontend_api.get_db_unique_name()
128
+
129
+ if options.db_name_prefix and options.db_name_prefix != "":
130
+ options.db_name_pattern = (
131
+ options.db_name_prefix.lower() + "_" + options.db_name_pattern
132
+ )
133
+ options.load_db_name_pattern = (
134
+ options.db_name_prefix.lower() + "_" + options.load_db_name_pattern
135
+ )
136
+
137
+ if not is_valid_path_for_logs(options.log_path):
138
+ raise OrchestrationConfigException(
139
+ f"Invalid value for OFFLOAD_LOGDIR: {options.log_path}"
140
+ )
141
+
142
+
143
+ def normalise_bigquery_options(options, exc_cls=OrchestrationConfigException):
144
+ if options.backend_distribution != BACKEND_DISTRO_GCP:
145
+ return
146
+ if options.google_dataproc_batches_version:
147
+ if not re.match(
148
+ r"^[1-9]\.[0-9](\.[0-9])?$", options.google_dataproc_batches_version
149
+ ):
150
+ raise exc_cls(
151
+ f"Invalid value for GOOGLE_DATAPROC_BATCHES_VERSION: {options.google_dataproc_batches_version}"
152
+ )
153
+ if options.google_dataproc_batches_ttl:
154
+ if not re.match(r"^[1-9][0-9]*[mhd]$", options.google_dataproc_batches_ttl):
155
+ raise exc_cls(
156
+ f"Invalid value for GOOGLE_DATAPROC_BATCHES_TTL: {options.google_dataproc_batches_ttl}"
157
+ )
158
+
159
+
160
+ def normalise_hadoop_options(options, exc_cls=OrchestrationConfigException):
161
+ if options.backend_distribution not in HADOOP_BASED_BACKEND_DISTRIBUTIONS:
162
+ return
163
+
164
+ if not options.hadoop_host:
165
+ raise exc_cls("HIVE_SERVER_HOST is mandatory")
166
+
167
+ if options.target == DBTYPE_HIVE:
168
+ if not options.hiveserver2_auth_mechanism:
169
+ options.hiveserver2_auth_mechanism = "PLAIN"
170
+ elif options.target == DBTYPE_IMPALA:
171
+ if not options.hiveserver2_auth_mechanism:
172
+ options.hiveserver2_auth_mechanism = "NOSASL"
173
+
174
+ # Select hadoop_host from (potentially) comma-separated list
175
+ options.hadoop_host = random.choice(options.hadoop_host.split(","))
176
+ options.hdfs_host = options.hdfs_host or options.hadoop_host
177
+
178
+ normalise_webhdfs(options)
179
+
180
+
181
+ def normalise_backend_session_parameters(opts):
182
+ if not opts.backend_session_parameters:
183
+ opts.backend_session_parameters = {}
184
+ elif isinstance(opts.backend_session_parameters, str):
185
+ verify_json_option(
186
+ "OFFLOAD_BACKEND_SESSION_PARAMETERS", opts.backend_session_parameters
187
+ )
188
+ opts.backend_session_parameters = json.loads(opts.backend_session_parameters)
189
+
190
+
191
+ def normalise_data_governance_config(options):
192
+ if options.data_governance_api_url:
193
+ if not options.data_governance_backend:
194
+ raise OrchestrationConfigException(
195
+ "DATA_GOVERNANCE_BACKEND is required when connecting to a Data Governance API"
196
+ )
197
+ if options.data_governance_backend not in SUPPORTED_DATA_GOVERNANCE_BACKENDS:
198
+ raise OrchestrationConfigException(
199
+ "Invalid DATA_GOVERNANCE_BACKEND %s. Valid values are: %s"
200
+ % (options.data_governance_backend, SUPPORTED_DATA_GOVERNANCE_BACKENDS)
201
+ )
202
+ if (
203
+ options.data_governance_backend == "navigator"
204
+ and not options.cloudera_navigator_hive_source_id
205
+ ):
206
+ raise OrchestrationConfigException(
207
+ "--cloudera-navigator-hive-source-id is required for Cloudera Navigator integration"
208
+ )
209
+
210
+ tag_list = (
211
+ options.data_governance_auto_tags_csv.split(",")
212
+ if options.data_governance_auto_tags_csv
213
+ else []
214
+ )
215
+ invalid_auto_tags = [_ for _ in tag_list if not is_valid_data_governance_tag(_)]
216
+ if invalid_auto_tags:
217
+ # Using env var name for exception because the command line option is hidden
218
+ raise OrchestrationConfigException(
219
+ "Invalid values for DATA_GOVERNANCE_AUTO_TAGS: %s" % invalid_auto_tags
220
+ )
221
+
222
+ auto_properties = (
223
+ options.data_governance_auto_properties_csv.split(",")
224
+ if options.data_governance_auto_properties_csv
225
+ else []
226
+ )
227
+ invalid_auto_props = [
228
+ _ for _ in auto_properties if not is_valid_data_governance_auto_property(_)
229
+ ]
230
+ # TODO nj@2020-01-31 It feels like we should add the lines below but need to investigate/test before we do
231
+ # if invalid_auto_props:
232
+ # raise OrchestrationConfigException('Invalid values for DATA_GOVERNANCE_AUTO_PROPERTIES: %s'
233
+ # % invalid_auto_props)
234
+
235
+
236
+ def normalise_filesystem_options(options, exc_cls=OrchestrationConfigException):
237
+ options.offload_fs_scheme = (
238
+ options.offload_fs_scheme.lower()
239
+ if options.offload_fs_scheme
240
+ else options.offload_fs_scheme
241
+ )
242
+ if (
243
+ options.offload_fs_scheme
244
+ and options.offload_fs_scheme not in VALID_OFFLOAD_FS_SCHEMES
245
+ ):
246
+ raise exc_cls(
247
+ "OFFLOAD_FS_SCHEME/--offload-fs-scheme must be one of: %s"
248
+ % VALID_OFFLOAD_FS_SCHEMES
249
+ )
250
+ if (
251
+ options.offload_fs_scheme in OFFLOAD_FS_SCHEMES_REQUIRING_CONTAINER
252
+ and not options.offload_fs_container
253
+ ):
254
+ raise exc_cls(
255
+ "OFFLOAD_FS_CONTAINER/--offload-fs-container required for scheme: %s"
256
+ % options.offload_fs_scheme
257
+ )
258
+ if (
259
+ options.offload_fs_scheme == OFFLOAD_FS_SCHEME_MAPRFS
260
+ and options.backend_distribution != BACKEND_DISTRO_MAPR
261
+ ):
262
+ raise exc_cls(
263
+ "OFFLOAD_FS_SCHEME/--offload-fs-scheme %s not valid for this backend system"
264
+ % options.offload_fs_scheme
265
+ )
266
+
267
+ if options.backend_distribution in HADOOP_BASED_BACKEND_DISTRIBUTIONS:
268
+ if not options.hdfs_data:
269
+ raise exc_cls("HDFS_DATA environment variable is mandatory")
270
+ if not options.hdfs_load:
271
+ raise exc_cls("HDFS_LOAD environment variable is mandatory")
272
+
273
+ # Not ideal to hardcode SNOWFLAKE here but we don't have a backend_api in normalise* to get the supported schemes
274
+ if options.backend_distribution == BACKEND_DISTRO_SNOWFLAKE:
275
+ if (
276
+ options.offload_fs_scheme in AZURE_OFFLOAD_FS_SCHEMES
277
+ and not options.offload_fs_azure_account_name
278
+ ):
279
+ raise exc_cls(
280
+ "OFFLOAD_FS_AZURE_ACCOUNT_NAME required for scheme: %s"
281
+ % options.offload_fs_azure_account_name
282
+ )
283
+ if (
284
+ options.offload_fs_scheme in AZURE_OFFLOAD_FS_SCHEMES
285
+ and not options.offload_fs_azure_account_key
286
+ ):
287
+ raise exc_cls(
288
+ "OFFLOAD_FS_AZURE_ACCOUNT_KEY required for scheme: %s"
289
+ % options.offload_fs_azure_account_key
290
+ )
291
+ if (
292
+ options.offload_fs_azure_account_domain
293
+ and not options.offload_fs_azure_account_domain.startswith(".")
294
+ ):
295
+ options.offload_fs_azure_account_domain = (
296
+ "." + options.offload_fs_azure_account_domain
297
+ )
298
+
299
+
300
+ def normalise_listener_options(options):
301
+ # Listener options
302
+ if options.listener_port:
303
+ options.listener_port = orchestration_defaults.posint_option_from_string(
304
+ "OFFLOAD_LISTENER_PORT", options.listener_port
305
+ )
306
+ if options.listener_heartbeat_interval:
307
+ options.listener_heartbeat_interval = (
308
+ orchestration_defaults.posint_option_from_string(
309
+ "OFFLOAD_LISTENER_HEARTBEAT_INTERVAL",
310
+ options.listener_heartbeat_interval,
311
+ )
312
+ )
313
+ if options.password_key_file:
314
+ pass_tool = PasswordTools()
315
+ goe_key = pass_tool.get_password_key_from_key_file(options.password_key_file)
316
+ if options.listener_shared_token:
317
+ options.listener_shared_token = pass_tool.b64decrypt(
318
+ options.listener_shared_token, goe_key
319
+ )
320
+ if options.listener_redis_password:
321
+ options.listener_redis_password = pass_tool.b64decrypt(
322
+ options.listener_redis_password, goe_key
323
+ )
324
+
325
+
326
+ def normalise_offload_transport_config(options, exc_cls=OrchestrationConfigException):
327
+ def simple_file_csv(var_val, var_name):
328
+ """Check for invalid characters that could be a shell injection risk."""
329
+ if not re.match(r"^[a-z0-9\/_\-:.,]+$", var_val):
330
+ raise exc_cls(f"Invalid characters found in {var_name}: {var_val}")
331
+
332
+ # Spark config
333
+ verify_json_option(
334
+ "OFFLOAD_TRANSPORT_RDBMS_SESSION_PARAMETERS",
335
+ options.offload_transport_rdbms_session_parameters,
336
+ )
337
+
338
+ if (
339
+ options.offload_transport_spark_submit_executable
340
+ and options.offload_transport_spark_submit_executable
341
+ not in VALID_SPARK_SUBMIT_EXECUTABLES
342
+ ):
343
+ raise exc_cls(
344
+ "Invalid value for OFFLOAD_TRANSPORT_SPARK_SUBMIT_EXECUTABLE: %s"
345
+ % options.offload_transport_spark_submit_executable
346
+ )
347
+ if options.offload_transport_spark_submit_master_url and not (
348
+ options.offload_transport_spark_submit_master_url
349
+ == SPARK_SUBMIT_SPARK_YARN_MASTER
350
+ or re.match(
351
+ r"^local(\[[0-9]\])?$", options.offload_transport_spark_submit_master_url
352
+ )
353
+ or re.match(
354
+ r"^(spark|mesos|k8s)://.+$",
355
+ options.offload_transport_spark_submit_master_url,
356
+ )
357
+ ):
358
+ raise exc_cls(
359
+ "Invalid value for OFFLOAD_TRANSPORT_SPARK_SUBMIT_MASTER_URL: %s"
360
+ % options.offload_transport_spark_submit_master_url
361
+ )
362
+
363
+ if options.offload_transport_spark_files:
364
+ simple_file_csv(
365
+ options.offload_transport_spark_files, "OFFLOAD_TRANSPORT_SPARK_FILES"
366
+ )
367
+
368
+ if options.offload_transport_spark_jars:
369
+ simple_file_csv(
370
+ options.offload_transport_spark_jars, "OFFLOAD_TRANSPORT_SPARK_JARS"
371
+ )
372
+
373
+ # Spark thriftserver config
374
+ if options.offload_transport_spark_thrift_host:
375
+ options.offload_transport_spark_thrift_host = random.choice(
376
+ options.offload_transport_spark_thrift_host.split(",")
377
+ )
378
+ if options.offload_transport_spark_thrift_port:
379
+ options.offload_transport_spark_thrift_port = (
380
+ orchestration_defaults.posint_option_from_string(
381
+ "OFFLOAD_TRANSPORT_SPARK_THRIFT_PORT",
382
+ options.offload_transport_spark_thrift_port,
383
+ )
384
+ )
385
+
386
+ # Livy config
387
+ if (
388
+ type(options.offload_transport_livy_api_verify_ssl) is not bool
389
+ and options.offload_transport_livy_api_verify_ssl is not None
390
+ ):
391
+ options.offload_transport_livy_api_verify_ssl = str(
392
+ options.offload_transport_livy_api_verify_ssl
393
+ ).strip()
394
+ if options.offload_transport_livy_api_verify_ssl == "":
395
+ options.offload_transport_livy_api_verify_ssl = None
396
+ elif options.offload_transport_livy_api_verify_ssl.lower() in ["true", "false"]:
397
+ options.offload_transport_livy_api_verify_ssl = bool(
398
+ options.offload_transport_livy_api_verify_ssl.lower() == "true"
399
+ )
400
+ if options.offload_transport_livy_api_verify_ssl is None:
401
+ options.offload_transport_livy_api_verify_ssl = (
402
+ derive_rest_api_verify_value_from_url(
403
+ options.offload_transport_livy_api_url
404
+ )
405
+ )
406
+ options.offload_transport_livy_max_sessions = (
407
+ orchestration_defaults.posint_option_from_string(
408
+ "OFFLOAD_TRANSPORT_LIVY_MAX_SESSIONS",
409
+ options.offload_transport_livy_max_sessions,
410
+ )
411
+ )
412
+ options.offload_transport_livy_idle_session_timeout = (
413
+ orchestration_defaults.posint_option_from_string(
414
+ "OFFLOAD_TRANSPORT_LIVY_IDLE_SESSION_TIMEOUT",
415
+ options.offload_transport_livy_idle_session_timeout,
416
+ )
417
+ )
418
+
419
+ # Generic transport config
420
+ if options.offload_transport:
421
+ options.offload_transport = options.offload_transport.upper()
422
+ if (
423
+ not options.offload_transport
424
+ or options.offload_transport not in VALID_OFFLOAD_TRANSPORTS
425
+ ):
426
+ raise exc_cls(
427
+ "Invalid value for OFFLOAD_TRANSPORT/--offload-transport: %s"
428
+ % options.offload_transport
429
+ )
430
+ options.offload_transport_cmd_host = (
431
+ options.offload_transport_cmd_host or options.hdfs_host
432
+ )
433
+
434
+ if not options.offload_transport_dsn:
435
+ if options.db_type == DBTYPE_MSSQL:
436
+ options.offload_transport_dsn = options.mssql_dsn
437
+ elif options.db_type == DBTYPE_ORACLE:
438
+ options.offload_transport_dsn = options.oracle_dsn
439
+ elif options.db_type == DBTYPE_TERADATA:
440
+ options.offload_transport_dsn = options.teradata_server
441
+
442
+ if options.offload_staging_format:
443
+ options.offload_staging_format = options.offload_staging_format.upper()
444
+
445
+ # For backward compatibility
446
+ options.offload_transport_user = (
447
+ options.offload_transport_user or options.hadoop_ssh_user
448
+ )
449
+ if not options.offload_transport_user:
450
+ raise exc_cls("OFFLOAD_TRANSPORT_USER is mandatory")
451
+
452
+
453
+ def normalise_rdbms_options(options, exc_cls=OrchestrationConfigException):
454
+ if not options.db_type:
455
+ options.db_type = orchestration_defaults.frontend_db_type_default()
456
+ if getattr(options, "use_oracle_wallet", None) is None:
457
+ options.use_oracle_wallet = False
458
+ # Source vendor specifics
459
+ if options.db_type == DBTYPE_MSSQL:
460
+ normalise_mssql_options(options, exc_cls=exc_cls)
461
+ elif options.db_type == DBTYPE_ORACLE:
462
+ normalise_rdbms_oracle_options(options, exc_cls=exc_cls)
463
+ elif options.db_type == DBTYPE_TERADATA:
464
+ normalise_teradata_options(options, exc_cls=exc_cls)
465
+
466
+
467
+ def normalise_rdbms_oracle_options(options, exc_cls=OrchestrationConfigException):
468
+ """Process Oracle connection options
469
+ silent is required when this is called before options has been defined, we don't want to try logging anything
470
+ before logging is ready.
471
+ """
472
+ if not options.oracle_dsn:
473
+ raise exc_cls("Oracle connection options required")
474
+ elif not options.use_oracle_wallet and (
475
+ not options.ora_adm_user
476
+ or not options.ora_adm_pass
477
+ or not options.ora_app_user
478
+ or not options.ora_app_pass
479
+ ):
480
+ raise exc_cls(
481
+ "Oracle username and password must be supplied for app and admin users unless Oracle Wallet is used"
482
+ )
483
+ elif not options.ora_repo_user:
484
+ raise exc_cls("Oracle repository username required")
485
+
486
+ options.rdbms_app_user = options.ora_app_user
487
+ options.rdbms_app_pass = options.ora_app_pass
488
+ if options.oracle_adm_dsn:
489
+ options.rdbms_dsn = options.oracle_adm_dsn
490
+ else:
491
+ options.rdbms_dsn = options.oracle_dsn
492
+
493
+ if not options.password_key_file:
494
+ return
495
+ pass_tool = PasswordTools()
496
+ goe_key = pass_tool.get_password_key_from_key_file(options.password_key_file)
497
+ options.rdbms_app_pass = options.ora_app_pass = pass_tool.b64decrypt(
498
+ options.rdbms_app_pass, goe_key
499
+ )
500
+ options.ora_adm_pass = pass_tool.b64decrypt(options.ora_adm_pass, goe_key)
501
+
502
+
503
+ def normalise_rdbms_wallet_options(options, frontend_api=None):
504
+ if options.use_oracle_wallet:
505
+ if frontend_api:
506
+ # We need to set/override ora_adm_user when using Oracle Wallet
507
+ options.ora_adm_user = frontend_api.get_session_user()
508
+ options.offload_transport_auth_using_oracle_wallet = True
509
+
510
+
511
+ def normalise_mssql_options(options, exc_cls=OrchestrationConfigException):
512
+ if (
513
+ not options.mssql_app_user
514
+ or not options.mssql_app_pass
515
+ or not options.mssql_dsn
516
+ ):
517
+ raise exc_cls("Microsoft SQL Server connection options required")
518
+
519
+ options.rdbms_app_user = options.mssql_app_user
520
+ options.rdbms_app_pass = options.mssql_app_pass
521
+ options.rdbms_dsn = options.mssql_dsn
522
+ options.ora_adm_user = None
523
+ options.ora_adm_pass = None
524
+ options.ora_repo_user = None
525
+ options.sqoop_password_file = None
526
+
527
+
528
+ def normalise_teradata_options(options, exc_cls=OrchestrationConfigException):
529
+ if (
530
+ not options.teradata_adm_user
531
+ or not options.teradata_adm_pass
532
+ or not options.teradata_server
533
+ ):
534
+ raise exc_cls("Teradata connection options required")
535
+ if not options.teradata_app_user or not options.teradata_app_pass:
536
+ raise exc_cls("Teradata connection options required")
537
+ if not options.teradata_repo_user:
538
+ raise exc_cls("Teradata repository username required")
539
+
540
+ options.rdbms_app_user = options.teradata_app_user
541
+ options.rdbms_app_pass = options.teradata_app_pass
542
+ options.rdbms_dsn = options.teradata_server
543
+
544
+ if not options.frontend_odbc_driver_name:
545
+ raise exc_cls("FRONTEND_ODBC_DRIVER_NAME is required for Teradata connections")
546
+
547
+ if not options.password_key_file:
548
+ return
549
+ pass_tool = PasswordTools()
550
+ goe_key = pass_tool.get_password_key_from_key_file(options.password_key_file)
551
+ options.rdbms_app_pass = options.teradata_app_pass = pass_tool.b64decrypt(
552
+ options.teradata_app_pass, goe_key
553
+ )
554
+ options.teradata_adm_pass = pass_tool.b64decrypt(options.teradata_adm_pass, goe_key)
555
+
556
+
557
+ def normalise_size_option(
558
+ opt_val, binary_sizes=False, strict_name=None, exc_cls=OrchestrationConfigException
559
+ ):
560
+ """strict_name can be used to raise an exception if incorrectly formatted inputs are
561
+ passed in. Otherwise we just return 0.
562
+ strict_name is the name of the option to include in the exception
563
+ """
564
+ if opt_val and type(opt_val) is str:
565
+ new_val = opt_val.upper() if opt_val else None
566
+ else:
567
+ new_val = opt_val
568
+ new_val = human_size_to_bytes(new_val, binary_sizes=binary_sizes)
569
+ if strict_name and new_val is None:
570
+ raise exc_cls("Invalid %s: %s" % (strict_name, opt_val))
571
+ return new_val or 0
572
+
573
+
574
+ def normalise_snowflake_options(options, exc_cls=OrchestrationConfigException):
575
+ if options.backend_distribution != BACKEND_DISTRO_SNOWFLAKE:
576
+ return
577
+ if not options.snowflake_user:
578
+ raise exc_cls("SNOWFLAKE_USER is mandatory")
579
+ if not options.snowflake_pass and not options.snowflake_pem_file:
580
+ raise exc_cls("Either SNOWFLAKE_PASS or SNOWFLAKE_PEM_FILE is required")
581
+ if not options.snowflake_account:
582
+ raise exc_cls("SNOWFLAKE_ACCOUNT is mandatory")
583
+ if not options.snowflake_database:
584
+ raise exc_cls("SNOWFLAKE_DATABASE is mandatory")
585
+ if not options.snowflake_file_format_prefix:
586
+ raise exc_cls("SNOWFLAKE_FILE_FORMAT_PREFIX is mandatory")
587
+ if not options.snowflake_integration:
588
+ raise exc_cls("SNOWFLAKE_INTEGRATION is mandatory")
589
+ if not options.snowflake_role:
590
+ raise exc_cls("SNOWFLAKE_ROLE is mandatory")
591
+ if not options.snowflake_stage:
592
+ raise exc_cls("SNOWFLAKE_STAGE is mandatory")
593
+ if not options.snowflake_warehouse:
594
+ raise exc_cls("SNOWFLAKE_WAREHOUSE is mandatory")
595
+
596
+
597
+ def normalise_synapse_options(options, exc_cls=OrchestrationConfigException):
598
+ if options.backend_distribution != BACKEND_DISTRO_MSAZURE:
599
+ return
600
+ if not options.synapse_auth_mechanism:
601
+ raise exc_cls("SYNAPSE_AUTH_MECHANISM is mandatory")
602
+ if options.synapse_auth_mechanism not in SYNAPSE_VALID_AUTH_MECHANISMS:
603
+ raise exc_cls(
604
+ "Invalid value for SYNAPSE_AUTH_MECHANISM: %s. Must be one of: %s"
605
+ % (options.synapse_auth_mechanism, ", ".join(SYNAPSE_VALID_AUTH_MECHANISMS))
606
+ )
607
+ if options.synapse_auth_mechanism in SYNAPSE_USER_PASS_AUTH_MECHANISMS:
608
+ if not options.synapse_user:
609
+ raise exc_cls("SYNAPSE_USER is mandatory")
610
+ if not options.synapse_pass:
611
+ raise exc_cls("SYNAPSE_PASS is mandatory")
612
+ if options.synapse_auth_mechanism == SYNAPSE_AUTH_MECHANISM_AD_SERVICE_PRINCIPAL:
613
+ if not options.synapse_service_principal_id:
614
+ raise exc_cls("SYNAPSE_SERVICE_PRINCIPAL_ID is mandatory")
615
+ if not options.synapse_service_principal_secret:
616
+ raise exc_cls("SYNAPSE_SERVICE_PRINCIPAL_SECRET is mandatory")
617
+ if not options.synapse_server:
618
+ raise exc_cls("SYNAPSE_SERVER is mandatory")
619
+ if not options.synapse_database:
620
+ raise exc_cls("SYNAPSE_DATABASE is mandatory")
621
+ if not options.synapse_port:
622
+ raise exc_cls("SYNAPSE_PORT is mandatory")
623
+ if not options.synapse_data_source:
624
+ raise exc_cls("SYNAPSE_DATA_SOURCE is mandatory")
625
+ if not options.synapse_file_format:
626
+ raise exc_cls("SYNAPSE_FILE_FORMAT is mandatory")
627
+ if not options.synapse_role:
628
+ raise exc_cls("SYNAPSE_ROLE is mandatory")
629
+ if not options.backend_odbc_driver_name:
630
+ raise exc_cls("BACKEND_ODBC_DRIVER_NAME is mandatory")
631
+
632
+
633
+ def normalise_webhdfs(options):
634
+ def webhdfs_default_port(webhdfs_verify_ssl):
635
+ default_http_port = 50070
636
+ default_https_port = 50470
637
+ return default_http_port if webhdfs_verify_ssl is None else default_https_port
638
+
639
+ if isinstance(options.webhdfs_verify_ssl, bool):
640
+ return
641
+ if options.webhdfs_verify_ssl is not None:
642
+ if options.webhdfs_verify_ssl.lower() in ["true", "false"]:
643
+ options.webhdfs_verify_ssl = (
644
+ True if options.webhdfs_verify_ssl.lower() == "true" else False
645
+ )
646
+ elif not str(options.webhdfs_verify_ssl).strip():
647
+ options.webhdfs_verify_ssl = None
648
+
649
+ options.webhdfs_port = (
650
+ options.webhdfs_port
651
+ or orchestration_defaults.webhdfs_port_default()
652
+ or webhdfs_default_port(options.webhdfs_verify_ssl)
653
+ )
654
+
655
+
656
+ def check_offload_fs_scheme_supported_in_backend(offload_fs_scheme, backend_api):
657
+ if offload_fs_scheme not in backend_api.valid_offload_fs_schemes():
658
+ display_version = backend_api.target_version()
659
+ display_version = " ({})".format(display_version) if display_version else ""
660
+ raise OrchestrationConfigException(
661
+ "Offload to %s%s does not support filesystem scheme: %s"
662
+ % (backend_api.backend_db_name(), display_version, offload_fs_scheme)
663
+ )
@@ -0,0 +1,38 @@
1
+ #! /usr/bin/env python3
2
+
3
+ # Copyright 2016 The GOE Authors. All rights reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ """ option_descriptions: Library of constants defining descriptions for options.
18
+ In the future we expect to refactor all option processing, including descriptions, and this module will
19
+ may become redundant at that time.
20
+ """
21
+
22
+ DATA_SAMPLE_PARALLELISM = (
23
+ "Degree of parallelism to use when sampling RDBMS data for columns with no precision/scale properties. "
24
+ "Values of 0 or 1 will execute the query without parallelism"
25
+ )
26
+
27
+ RESET_BACKEND_TABLE = "Remove backend data table. Use with caution - this will delete previously offloaded data for this table!"
28
+
29
+ REUSE_BACKEND_TABLE = (
30
+ "Allow Offload to re-use an empty backend table when there is already Offload metadata. "
31
+ "This may be useful if a backend table had data removed by an administrator and a re-offload is required"
32
+ )
33
+
34
+ VERIFY_PARALLELISM = (
35
+ "Degree of parallelism to use for the RDBMS query executed when validating an offload. "
36
+ "Values of 0 or 1 will execute the query without parallelism. Values > 1 will force a parallel query of the given degree. "
37
+ "If unset, the RDBMS query will fall back to using the behavior specified by RDBMS defaults"
38
+ )