airflow-unicore-integration 0.2.0__py3-none-any.whl → 0.2.2__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.
@@ -10,16 +10,17 @@ tasks should be allowed to overwrite SITE, CREDENTIALS_*, UNICORE_CONN_ID and DE
10
10
  import time
11
11
  from typing import Any
12
12
  from typing import Dict
13
- from typing import List
14
13
 
15
14
  import pyunicore.client as uc_client
16
15
  from airflow.configuration import conf
17
16
  from airflow.executors.base_executor import BaseExecutor
18
17
  from airflow.executors.workloads import All
19
18
  from airflow.executors.workloads import ExecuteTask
19
+ from airflow.models.taskinstance import TaskInstance
20
20
  from airflow.models.taskinstancekey import TaskInstanceKey
21
21
  from airflow.utils.state import TaskInstanceState
22
22
  from pyunicore import client
23
+ from pyunicore.credentials import Credential
23
24
  from pyunicore.credentials import create_credential
24
25
 
25
26
  from ..util.job import JobDescriptionGenerator
@@ -39,37 +40,74 @@ STATE_MAPPINGS: Dict[uc_client.JobStatus, TaskInstanceState] = {
39
40
 
40
41
  class UnicoreExecutor(BaseExecutor):
41
42
 
42
- EXECUTOR_CONFIG_UNICORE_CONN_KEY = (
43
- "unicore_connection_id" # alternative connection id for the Unicore connection to use
44
- )
45
43
  EXECUTOR_CONFIG_UNICORE_SITE_KEY = "unicore_site" # alternative Unicore site to run at, only required if different than connection default
46
44
  EXECUTOR_CONFIG_UNICORE_CREDENTIAL_KEY = "unicore_credential" # alternative unicore credential to use for the job, only required if different than connection default
47
45
 
46
+ serve_logs = True
47
+
48
48
  def start(self):
49
49
  self.active_jobs: Dict[TaskInstanceKey, uc_client.Job] = {}
50
50
  # TODO get job description generator class and init params from config
51
51
  self.job_descr_generator: JobDescriptionGenerator = NaiveJobDescriptionGenerator()
52
52
 
53
+ def _handle_used_compute_time(self, task: TaskInstanceKey, job: uc_client.Job) -> None:
54
+ pass
55
+
53
56
  def sync(self) -> None:
54
57
  # iterate through task collection and update task/ job status - delete if needed
55
58
  for task, job in list(self.active_jobs.items()):
56
59
  state = STATE_MAPPINGS[job.status]
57
60
  if state == TaskInstanceState.FAILED:
58
61
  self.fail(task)
59
- self._forward_unicore_log(task, job)
60
62
  self.active_jobs.pop(task)
63
+ self._handle_used_compute_time(task, job)
61
64
  elif state == TaskInstanceState.SUCCESS:
62
65
  self.success(task)
63
- self._forward_unicore_log(task, job)
64
66
  self.active_jobs.pop(task)
65
- elif state == TaskInstanceState.RUNNING:
67
+ self._handle_used_compute_time(task, job)
68
+ elif state == TaskInstanceState.QUEUED:
66
69
  self.running_state(task, state)
67
70
 
68
71
  return super().sync()
69
72
 
70
- def _forward_unicore_log(self, task: TaskInstanceKey, job: uc_client.Job) -> List[str]:
71
- # TODO retrieve unicore logs from job directory and return
72
- return []
73
+ def get_task_log(self, ti: TaskInstance, try_number: int) -> tuple[list[str], list[str]]:
74
+ # if task is still active, no neeed to check other jobs for match
75
+ job: uc_client.Job | None = None
76
+ if ti.key in self.active_jobs:
77
+ job = self.active_jobs[ti.key]
78
+ # if job is finished, need to search all jobs (that are still present on the target system)
79
+ else:
80
+ job = self._get_job_from_id(ti.key)
81
+
82
+ if not job:
83
+ return [], []
84
+
85
+ # TODO return filecontent for stdout and stderr from jobdirectory
86
+ logs = job.properties["log"] # type: ignore
87
+ working_dir = job.working_dir
88
+ stdout = working_dir.stat("/stdout")
89
+ stderr = working_dir.stat("/stderr")
90
+
91
+ lines_out = stdout.raw().read().decode("utf-8").split("\n") # type: ignore
92
+ lines_err = stderr.raw().read().decode("utf-8").split("\n") # type: ignore
93
+
94
+ return logs + lines_out + lines_err, ["Test", "Where is this shown??"]
95
+
96
+ def _get_job_from_id(self, key: TaskInstanceKey) -> uc_client.Job | None:
97
+ # iterate over jobs and check if key matches job name
98
+ unicore_client = self._get_unicore_client()
99
+ # check 50 jobs at a time to prevent server from not returning some jobs
100
+ offset = 0
101
+ number = 50
102
+ job_name = self.job_descr_generator.get_job_name(key)
103
+ while True:
104
+ partial_job_list = unicore_client.get_jobs(offset=offset, num=number)
105
+ if len(partial_job_list) == 0:
106
+ return None
107
+ offset += len(partial_job_list)
108
+ for job in partial_job_list:
109
+ if job.properties["name"] == job_name: # type: ignore
110
+ return job
73
111
 
74
112
  def _get_unicore_client(self, executor_config: dict | None = {}):
75
113
  overwrite_unicore_site = executor_config.get( # type: ignore
@@ -79,25 +117,29 @@ class UnicoreExecutor(BaseExecutor):
79
117
  UnicoreExecutor.EXECUTOR_CONFIG_UNICORE_CREDENTIAL_KEY, None
80
118
  ) # task can provide a different credential to use, else default from connection is used
81
119
  token = conf.get("unicore.executor", "AUTH_TOKEN", fallback="")
120
+ if overwrite_unicore_credential is not None:
121
+ token = overwrite_unicore_credential
122
+ self.log.debug("Using user provided token.")
82
123
  base_url = conf.get(
83
124
  "unicore.executor", "DEFAULT_URL", fallback="http://localhost:8080/DEMO-SITE/rest/core"
84
125
  )
85
- credential = create_credential(token=token)
126
+ credential: Credential = create_credential(token=token)
86
127
  if overwrite_unicore_site is not None:
87
128
  base_url = overwrite_unicore_site
88
- if overwrite_unicore_credential is not None:
89
- credential = overwrite_unicore_credential
129
+ self.log.debug("Using user provided site.")
90
130
  if not base_url:
91
131
  raise TypeError()
132
+ self.log.debug(f"Using site: {base_url}")
133
+ self.log.debug(f"Using credential: {credential}")
92
134
  conn = client.Client(credential, base_url)
93
135
  return conn
94
136
 
95
137
  def _submit_job(self, workload: ExecuteTask):
96
- uc_client = self._get_unicore_client(executor_config=workload.ti.executor_config)
138
+ unicore_client = self._get_unicore_client(executor_config=workload.ti.executor_config)
97
139
  job_descr = self._create_job_description(workload)
98
140
  self.log.info("Generated job description")
99
141
  self.log.debug(str(job_descr))
100
- job = uc_client.new_job(job_descr)
142
+ job = unicore_client.new_job(job_descr)
101
143
  self.log.info("Submitted unicore job")
102
144
  self.active_jobs[workload.ti.key] = job
103
145
  return job
@@ -28,6 +28,7 @@ class UnicoreHook(BaseHook):
28
28
  def __init__(self, uc_conn_id: str = default_conn_name) -> None:
29
29
  super().__init__()
30
30
  self.uc_conn_id = uc_conn_id
31
+ self.get_connection(self.uc_conn_id)
31
32
 
32
33
  @classmethod
33
34
  def get_connection_form_fields(cls):
@@ -38,9 +39,7 @@ class UnicoreHook(BaseHook):
38
39
  """Return custom UI field behaviour for UNICORE connection."""
39
40
  return {
40
41
  "hidden_fields": ["schema", "port", "extra"],
41
- "relabeling": {
42
- "login": "Username",
43
- },
42
+ "relabeling": {"login": "Username", "host": "Site URL"},
44
43
  "placeholder": {"auth_token": "UNICORE auth token"},
45
44
  }
46
45
 
@@ -77,6 +76,11 @@ class UnicoreHook(BaseHook):
77
76
  credential = credentials.create_credential(token=auth_token)
78
77
  return credential
79
78
 
79
+ def get_token(self) -> str:
80
+ """Returns a token if present, None if not."""
81
+ params = self.get_connection(self.uc_conn_id)
82
+ return params.extra_dejson.get("auth_token", None)
83
+
80
84
  def get_base_url(self) -> str:
81
85
  """Return the base url of the connection."""
82
86
  params = self.get_connection(self.uc_conn_id)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: airflow-unicore-integration
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Running Unicore Jobs from airflow DAGs.
5
5
  Author-email: Christian Böttcher <c.boettcher@fz-juelich.de>
6
6
  License-Expression: BSD-3-Clause
@@ -1,18 +1,18 @@
1
1
  airflow_unicore_integration/__init__.py,sha256=Qy1mlyxe2Y-PPSn0LgIW0sT6BxFuwW4_LGsPBf-Wm4s,549
2
2
  airflow_unicore_integration/executors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  airflow_unicore_integration/executors/run_task_via_supervisor.py,sha256=3ErgPf-Oy3B4Di5yNXhhPkaojIJykvCxMZ9MlKSYPI8,2756
4
- airflow_unicore_integration/executors/unicore_executor.py,sha256=E1nOskWSBmC-ReLRvA8E3bY-G0lpxP403tazlBNhgFQ,5919
4
+ airflow_unicore_integration/executors/unicore_executor.py,sha256=JaR8_503WMtI_emvcrzwJkZlm2q3c_e-3aRhP70y54k,7790
5
5
  airflow_unicore_integration/hooks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- airflow_unicore_integration/hooks/unicore_hooks.py,sha256=NEJ6sgQCD9AyZO9TA3vOR8RdijqaIX_FqY-Abz74Yys,3148
6
+ airflow_unicore_integration/hooks/unicore_hooks.py,sha256=ot9j8sG--_eTmmfoY5TpJj8BPo8nfYvjOSmqHyyQhBM,3383
7
7
  airflow_unicore_integration/operators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  airflow_unicore_integration/operators/container.py,sha256=D-2La59XZazXvBDVn-_wFIZ0IrCYeGy18miK8S5Usmc,693
9
9
  airflow_unicore_integration/operators/unicore_operators.py,sha256=PrcB-riakRUt8DHR9Hkk0ccOe-xk6NjJmNAmDpNgpN4,16868
10
10
  airflow_unicore_integration/policies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  airflow_unicore_integration/util/job.py,sha256=vYyaNzGw2EQWQlDTGO6q4T1gZTHoB9-RIb5KYYd6pAc,10556
12
12
  airflow_unicore_integration/util/launch_script_content.py,sha256=42_aFpaCMmvFmmUxQDGcudkleX1YSK_yYWE8T41NOy0,2915
13
- airflow_unicore_integration-0.2.0.dist-info/licenses/LICENSE,sha256=hZ5ouAedeNr8ClHrQE-RLsgMsARcmv3kSZz7tE2BTJE,1526
14
- airflow_unicore_integration-0.2.0.dist-info/METADATA,sha256=-ifD7f7RX30qQs3OMzsXKeAvo_acGHLigthKu93-rU0,13696
15
- airflow_unicore_integration-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
- airflow_unicore_integration-0.2.0.dist-info/entry_points.txt,sha256=PzEfCLYLSawjiYR-HNBzw8-YGfJxs1nPBULevgBQjoY,147
17
- airflow_unicore_integration-0.2.0.dist-info/top_level.txt,sha256=j45X-uIuOk3oL78iwlpHakMWtUkg__B7zUlJLwmZx6w,28
18
- airflow_unicore_integration-0.2.0.dist-info/RECORD,,
13
+ airflow_unicore_integration-0.2.2.dist-info/licenses/LICENSE,sha256=hZ5ouAedeNr8ClHrQE-RLsgMsARcmv3kSZz7tE2BTJE,1526
14
+ airflow_unicore_integration-0.2.2.dist-info/METADATA,sha256=FeW3Nt-3gyVI6gWTeWd_twhzUNEIVHqEJyPP6XMzvw8,13696
15
+ airflow_unicore_integration-0.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
+ airflow_unicore_integration-0.2.2.dist-info/entry_points.txt,sha256=PzEfCLYLSawjiYR-HNBzw8-YGfJxs1nPBULevgBQjoY,147
17
+ airflow_unicore_integration-0.2.2.dist-info/top_level.txt,sha256=j45X-uIuOk3oL78iwlpHakMWtUkg__B7zUlJLwmZx6w,28
18
+ airflow_unicore_integration-0.2.2.dist-info/RECORD,,