ocrd 3.7.0__py3-none-any.whl → 3.8.0__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.
- ocrd/cli/network.py +2 -0
- ocrd/cli/resmgr.py +29 -65
- ocrd/constants.py +0 -2
- ocrd/processor/base.py +6 -16
- ocrd/processor/builtin/dummy/ocrd-tool.json +25 -0
- ocrd/processor/builtin/merge_processor.py +131 -0
- ocrd/processor/builtin/param_command_header2unordered.json +7 -0
- ocrd/processor/builtin/param_command_heading2unordered.json +7 -0
- ocrd/processor/builtin/param_command_lines2orientation.json +6 -0
- ocrd/processor/builtin/param_command_page-update-version.json +5 -0
- ocrd/processor/builtin/param_command_transkribus-to-prima.json +8 -0
- ocrd/processor/builtin/shell_processor.py +128 -0
- ocrd/resource_manager.py +213 -124
- {ocrd-3.7.0.dist-info → ocrd-3.8.0.dist-info}/METADATA +22 -3
- {ocrd-3.7.0.dist-info → ocrd-3.8.0.dist-info}/RECORD +33 -25
- {ocrd-3.7.0.dist-info → ocrd-3.8.0.dist-info}/entry_points.txt +2 -0
- ocrd_models/ocrd_agent.py +3 -3
- ocrd_network/__init__.py +1 -0
- ocrd_network/cli/__init__.py +2 -0
- ocrd_network/cli/resmgr_server.py +23 -0
- ocrd_network/constants.py +3 -0
- ocrd_network/logging_utils.py +5 -0
- ocrd_network/resource_manager_server.py +182 -0
- ocrd_network/runtime_data/connection_clients.py +1 -1
- ocrd_network/runtime_data/hosts.py +43 -16
- ocrd_network/runtime_data/network_agents.py +15 -1
- ocrd_utils/__init__.py +5 -1
- ocrd_utils/constants.py +5 -0
- ocrd_utils/os.py +141 -61
- ocrd_validators/ocrd_tool.schema.yml +7 -4
- ocrd/resource_list.yml +0 -61
- {ocrd-3.7.0.dist-info → ocrd-3.8.0.dist-info}/LICENSE +0 -0
- {ocrd-3.7.0.dist-info → ocrd-3.8.0.dist-info}/WHEEL +0 -0
- {ocrd-3.7.0.dist-info → ocrd-3.8.0.dist-info}/top_level.txt +0 -0
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
ocrd/__init__.py,sha256=ZswMVmlqFhAEIzMR3my6IKPq9XLH21aDPC_m_8Jh4dA,1076
|
|
2
|
-
ocrd/constants.py,sha256=
|
|
2
|
+
ocrd/constants.py,sha256=REPY-y28MMsrTWBNB4oOsvX3W06Xr2fvtv9wuWH9oAI,633
|
|
3
3
|
ocrd/mets_server.py,sha256=LbZ0U2_o0W7cWO639U7E816dXabro8-8yHGX0quvHn4,22304
|
|
4
4
|
ocrd/ocrd-all-tool.json,sha256=EYXmMzP68p3KzL8nUZ16TCX2chQzKkAeISvuXqI_yIw,2094
|
|
5
5
|
ocrd/resolver.py,sha256=7uwHRxaK8YMdKHe_a2dfrcNwL6UhQRJRVBrIX7GST7Q,15443
|
|
6
|
-
ocrd/
|
|
7
|
-
ocrd/resource_manager.py,sha256=kIWDoKxWH4IJE1gcoTcCRQjYjieCqiQclyuyF6Y9b8A,16813
|
|
6
|
+
ocrd/resource_manager.py,sha256=2wo3JSCYE1oA0VgI8H901IsC-fnx6vRJ5qSMFgYNorE,20664
|
|
8
7
|
ocrd/task_sequence.py,sha256=r4e4iaP9AXzTL2xQZpfYnHuFXty5pE-ym3gIyUz1aJc,7180
|
|
9
8
|
ocrd/workspace.py,sha256=UL_gX0KA-MmpayBl9KGYTfcl-1Canj8S991G9RHhu70,65216
|
|
10
9
|
ocrd/workspace_backup.py,sha256=aUOnYeJ-nWu-Zve27B0cYd9ZtBkmQX4F4Wim2UcrR5I,3624
|
|
11
10
|
ocrd/workspace_bagger.py,sha256=4viSQoWteW0V4B_blB6asJXd4-qniGGJyCPfKnrsyrY,12054
|
|
12
11
|
ocrd/cli/__init__.py,sha256=-n2jpGBZs_OMpI31E7CljGVdoFxDhgCAYwibcl_vp1Q,2838
|
|
13
12
|
ocrd/cli/bashlib.py,sha256=sEpTKbqM5DEo6838Ki5aFU8QsokA2SqQ841gcBu7M5M,1148
|
|
14
|
-
ocrd/cli/network.py,sha256=
|
|
13
|
+
ocrd/cli/network.py,sha256=HA-JeyedsZksTQzuDoSBwyxEHIyIlc7oCmvNNDMA4vA,615
|
|
15
14
|
ocrd/cli/ocrd_tool.py,sha256=kB3Y3tj7Fpz6Ts4KgVlznhXpAx8gCDvJTnO39j8SGL4,7679
|
|
16
15
|
ocrd/cli/process.py,sha256=yfhBSYmuY5k2AccKwiNvG9hCDx1coYyWjq9BBwYaL3Y,1234
|
|
17
|
-
ocrd/cli/resmgr.py,sha256=
|
|
16
|
+
ocrd/cli/resmgr.py,sha256=7hRRi8EryQwakRdZgguee3ercA5_T48BKGWWfgAVfzM,8072
|
|
18
17
|
ocrd/cli/validate.py,sha256=P8jrzAnoU-5TUjLNA7s_ZMY2Krw5Y-SVIZPhdOk25cw,5931
|
|
19
18
|
ocrd/cli/workspace.py,sha256=0UzKN7vvD0n5wwxldzLHOlikDDIyiBiV1PuTOKCnnnE,41279
|
|
20
19
|
ocrd/cli/zip.py,sha256=3HMUbVsPTK3SRuF5oZnCZLjoqXJK-AYpA-rMqenY858,5965
|
|
@@ -24,19 +23,26 @@ ocrd/decorators/mets_find_options.py,sha256=8fiSdk-415o6-iBPB2T9He_v52qE8cTj3cCn
|
|
|
24
23
|
ocrd/decorators/ocrd_cli_options.py,sha256=Bemkq3V3QkOI3nNqGzphaNW7gjU9vNN-M5F2DvxvioM,2479
|
|
25
24
|
ocrd/decorators/parameter_option.py,sha256=TnCIcV9L5oAnI1Ew2TyFzo5FAwiIzWl2pn8oaD9jfEU,1056
|
|
26
25
|
ocrd/processor/__init__.py,sha256=39ymNwYRdc-b_OJzzKmWCvo2ga3KdsGSYDHE1Hzkn_w,274
|
|
27
|
-
ocrd/processor/base.py,sha256=
|
|
26
|
+
ocrd/processor/base.py,sha256=DxBsRn8VLsfNvc9_2BU0KxUv4t9XtHSSu9uiabxn8Nk,59850
|
|
28
27
|
ocrd/processor/helpers.py,sha256=4lR_QvZsxvh7f8_uK9YzdHP5-hvFU4qqYM_Cu_k41KI,10937
|
|
29
28
|
ocrd/processor/ocrd_page_result.py,sha256=qo9pGV4r9S5--NAq5clIJOfs4b1vavoDOTbDqAEAAKA,507
|
|
30
29
|
ocrd/processor/builtin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
30
|
ocrd/processor/builtin/dummy_processor.py,sha256=SmMRtN0w88kBU24654ThT-yf84SFsFW4BOcmwsDDWdc,3533
|
|
32
31
|
ocrd/processor/builtin/filter_processor.py,sha256=9mbMq_XTJa8wrlbNdf46GUMNdjedz-enxafsCrnNhEo,4295
|
|
32
|
+
ocrd/processor/builtin/merge_processor.py,sha256=UvYgB73Y9lzJSMMNaVO6nk3rAcONHvQ-E-XAhaseZno,4655
|
|
33
|
+
ocrd/processor/builtin/param_command_header2unordered.json,sha256=K6xEcDXc3Qsaxt96wdISK22UxHf517O-E8JHryDwAfE,307
|
|
34
|
+
ocrd/processor/builtin/param_command_heading2unordered.json,sha256=nBenDJYlV-POfoM2R6izxcA5pW3rz-czGaoKudj3OTY,309
|
|
35
|
+
ocrd/processor/builtin/param_command_lines2orientation.json,sha256=2cGv5fXqAVAUyP8K1IT6vrSLCGhAtdXAvVxdSfjp8KQ,282
|
|
36
|
+
ocrd/processor/builtin/param_command_page-update-version.json,sha256=4JsXEltEOG89Q8PT8eBxQXnxp3Mez3EsRz4GfshjJEY,267
|
|
37
|
+
ocrd/processor/builtin/param_command_transkribus-to-prima.json,sha256=AvPNNS5uBmh69i6irRhIdHN9gE28yN9ufV7jfpFAeME,472
|
|
38
|
+
ocrd/processor/builtin/shell_processor.py,sha256=aWsB_m7o4ypG1DBAE0sNMFnaw9ptONqchLLl06KgTEo,5888
|
|
33
39
|
ocrd/processor/builtin/dummy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
-
ocrd/processor/builtin/dummy/ocrd-tool.json,sha256=
|
|
40
|
+
ocrd/processor/builtin/dummy/ocrd-tool.json,sha256=t_M3HABw7k_Ufi1L9Mr4t3LSCRnu0HH8-fvEs3u2PQY,3487
|
|
35
41
|
ocrd_modelfactory/__init__.py,sha256=sjAwPwDzetvPHdV6nPquHtMdFUBYRmo1P-VKER9YCWM,4404
|
|
36
42
|
ocrd_models/__init__.py,sha256=A0aj0mOraNb-xfiUueACdoaqISnp0qH-F49nTJg2vCs,380
|
|
37
43
|
ocrd_models/constants.py,sha256=R7-jOGabFd8HP0qRWfTMk0RcUmdwN-jhmDVbUW_QfU4,6961
|
|
38
44
|
ocrd_models/mets-empty.xml,sha256=dFixfbxSXrgjZx9BfdIKWHX-khNmp7dNYaFe2qQSwCY,1203
|
|
39
|
-
ocrd_models/ocrd_agent.py,sha256=
|
|
45
|
+
ocrd_models/ocrd_agent.py,sha256=Nm0XDNCmWZ8O3xsXaY-WmEghttXmh90UKmAObCL99IY,5617
|
|
40
46
|
ocrd_models/ocrd_exif.py,sha256=HSLPn_WBDRIlMtKNYilLHm8WjX-b14HgnqT_KfzjS_0,4680
|
|
41
47
|
ocrd_models/ocrd_file.py,sha256=9-mfDb91RVy3p9rKryl-C39P4Of6Rb8OZBuxAee4VrI,9723
|
|
42
48
|
ocrd_models/ocrd_mets.py,sha256=lz9mlDq9A9UmZDoN8lh5XRnBzdAtLLZywDZSbyZPS84,50905
|
|
@@ -46,24 +52,26 @@ ocrd_models/ocrd_xml_base.py,sha256=iOnDl2zBNhN-Q4moLWiFkSqXvfRzxE5wbp5Tjsu1W6A,
|
|
|
46
52
|
ocrd_models/report.py,sha256=CX-t9ZDi2VmAy8M1Azsh83UsvE_f5pMeEC7tPaA-ztU,2021
|
|
47
53
|
ocrd_models/utils.py,sha256=A-H11ZJ65ZjH4DPK9s_Yz6JtA9fbTQ2jY-__9s7Hrg8,2320
|
|
48
54
|
ocrd_models/xpath_functions.py,sha256=VM2f9hl8ja4NrDOEQRSYdx7GewwAxfoyGMDjqjgA_7g,1439
|
|
49
|
-
ocrd_network/__init__.py,sha256=
|
|
55
|
+
ocrd_network/__init__.py,sha256=NWlSgXi7z45ow37AmITxfCB1d-L39rO8ttyxNJ-z8G0,376
|
|
50
56
|
ocrd_network/client.py,sha256=pL-g79cQgulXyGYgLOh--oxl1hZEMu48PTbuvMW1jIE,3007
|
|
51
57
|
ocrd_network/client_utils.py,sha256=Ne1a0fteb-TBuc0EAD6X_fh2RAl4hmPt2oluhpB28iU,5371
|
|
52
|
-
ocrd_network/constants.py,sha256=
|
|
58
|
+
ocrd_network/constants.py,sha256=XyRYjFO38yIBD6s1wsA-z6V16tBmbUw4LXlFkj-tQC8,1943
|
|
53
59
|
ocrd_network/database.py,sha256=-SddvaMLKn0pjdONyvWmjxfPJd6viedAIp6Lj1sU1Zs,10705
|
|
54
|
-
ocrd_network/logging_utils.py,sha256=
|
|
60
|
+
ocrd_network/logging_utils.py,sha256=hXwS46FzY_HTh92DgnxTuARxj8C18bOBmFKVrvBlUgc,2409
|
|
55
61
|
ocrd_network/param_validators.py,sha256=Jl1VwiPPKJ50k-xEHLdvW-1QDOkJHCiMz4k9Ipqm-Uc,1489
|
|
56
62
|
ocrd_network/process_helpers.py,sha256=t2qltUpRefzLwdSGsiUEOGYO4Pz2OH7arpgjmCAeXMU,3086
|
|
57
63
|
ocrd_network/processing_server.py,sha256=z21DvRleEeo0hkpc1-2z0jLKuf5WSipL95MVEns8eJE,38457
|
|
58
64
|
ocrd_network/processing_worker.py,sha256=5AtvIhfcePzltKj4SElh7Aj9zlUOEiMVPTjtXuFSbT8,12659
|
|
65
|
+
ocrd_network/resource_manager_server.py,sha256=Ihz2g9uhkPSJee9GL7485DFC4cORhro4JQI6QzHoUA4,7255
|
|
59
66
|
ocrd_network/server_cache.py,sha256=orfAMw3LwUnduRHFAB6MpfoORTDoPV4ntSdAcQHBOyI,13148
|
|
60
67
|
ocrd_network/server_utils.py,sha256=Lxby62gHvrSbHgpWXvyZGdsWajp2TFzyxjHdMZWBESk,10229
|
|
61
68
|
ocrd_network/tcp_to_uds_mets_proxy.py,sha256=yRW-O6ihd31gf7xqQBIBb_ZQQgqisMyOdRI216ehq_A,3160
|
|
62
69
|
ocrd_network/utils.py,sha256=yE-nV_sv171tPp7weIFOxYw6HJlxvGBmrS8b1rIHS7c,6760
|
|
63
|
-
ocrd_network/cli/__init__.py,sha256=
|
|
70
|
+
ocrd_network/cli/__init__.py,sha256=VBjjXcn-2O5gerqE6UdNfS-EkVFEVPQFHylsn8F9kfY,317
|
|
64
71
|
ocrd_network/cli/client.py,sha256=H5fiJhBqbFn4_B2p3V20GejGTIYO-mNglh3y5nzUGhs,10350
|
|
65
72
|
ocrd_network/cli/processing_server.py,sha256=NsuI0f9h4KDwe39YugmHo5cJ_29chcLLQ7DThKfPO7s,770
|
|
66
73
|
ocrd_network/cli/processing_worker.py,sha256=ZuaCkbKV_WKJV7cGOjZ6RLrjjppymnwNCiznFMlclAg,1897
|
|
74
|
+
ocrd_network/cli/resmgr_server.py,sha256=sc0VX_RehTbg8Qp7ht_DvVqsrdL5b9Zw3bBgWcAD13A,826
|
|
67
75
|
ocrd_network/models/__init__.py,sha256=eVYMZaktzlyHKx-zI7GLYyRlZd3Vi_lNgsqSSFwqb6U,475
|
|
68
76
|
ocrd_network/models/job.py,sha256=9bwp8DFoRH96WnRpkDV3XRfXCBiupzK6WXjqPsTcvLg,4440
|
|
69
77
|
ocrd_network/models/messages.py,sha256=OUDTjUiaATStsSAHCEDilUhBSruPsjpBtIBsllqN2Z0,672
|
|
@@ -78,20 +86,20 @@ ocrd_network/rabbitmq_utils/ocrd_messages.py,sha256=wwzfMWbXmOFo_nd32_XySCso91_U
|
|
|
78
86
|
ocrd_network/rabbitmq_utils/publisher.py,sha256=mw4XQQhRE1xUQVgEUseyG845iIgVO-9GdGwNH6nUFms,2433
|
|
79
87
|
ocrd_network/runtime_data/__init__.py,sha256=PnWuuagElbkTzGtPWQEk5wlFtDxqT7B48S0Zrgt8H68,320
|
|
80
88
|
ocrd_network/runtime_data/config_parser.py,sha256=Vr0FbsqmsoiuhDgZ7KFdeFZj9JvUulcOS2PCRFQQNHY,2364
|
|
81
|
-
ocrd_network/runtime_data/connection_clients.py,sha256=
|
|
89
|
+
ocrd_network/runtime_data/connection_clients.py,sha256=HKf_aSfwg11JeH6qiQXnqxbnvNgCAMRnVIpj30k93Bc,4207
|
|
82
90
|
ocrd_network/runtime_data/deployer.py,sha256=j3tcauURZtu7MKcEIE9B5eMCMSYMbxhB8LmtK72Zk1c,5314
|
|
83
|
-
ocrd_network/runtime_data/hosts.py,sha256=
|
|
84
|
-
ocrd_network/runtime_data/network_agents.py,sha256=
|
|
91
|
+
ocrd_network/runtime_data/hosts.py,sha256=P1bLh1NjgL-ajgP-VhGCACvy6rgJ5nZhGKsHaldzatk,8921
|
|
92
|
+
ocrd_network/runtime_data/network_agents.py,sha256=wwN7IJei4UdlyfhjvdmB5TB4O0Gn8icSkArJe-suvAY,4523
|
|
85
93
|
ocrd_network/runtime_data/network_services.py,sha256=5aH3RNGCi1fBuSdRp_Xz0MzyD_FmnvPnaBYAiYY3gp4,7891
|
|
86
|
-
ocrd_utils/__init__.py,sha256=
|
|
94
|
+
ocrd_utils/__init__.py,sha256=Cl_lrZxjXuTZ_me4I_lpaFNTpSdacWhQetOtHdrkUsU,6057
|
|
87
95
|
ocrd_utils/config.py,sha256=Oe8JBGb8r4z274XNWcdMV-GApzxmAYO8hHmbAV5bXf8,12609
|
|
88
|
-
ocrd_utils/constants.py,sha256=
|
|
96
|
+
ocrd_utils/constants.py,sha256=6lqMLeJdkFBlvGVmGjcExWbRKzNU6QT0kADBb5BkcBc,3464
|
|
89
97
|
ocrd_utils/deprecate.py,sha256=luAqGWUSF-9DHmTd2lDiQoQPA5SrJazdoDPQYQ6A7Z4,1029
|
|
90
98
|
ocrd_utils/image.py,sha256=tG5WnNtrrvGjm2-r6NVs1Jm7z8fee3MuLKotAD6C2RU,24818
|
|
91
99
|
ocrd_utils/introspect.py,sha256=LPhgcUuoicQcURDCWlCpSdbfVyxID5vmQPXJ9vzuYV0,1977
|
|
92
100
|
ocrd_utils/logging.py,sha256=-cCi_9kIzmLUixfnDcx2jq9IQuwMqrU-71RJhKOQilQ,7929
|
|
93
101
|
ocrd_utils/ocrd_logging.conf,sha256=JlWmA_5vg6HnjPGjTC4mA5vFHqmnEinwllSTiOw5CCo,3473
|
|
94
|
-
ocrd_utils/os.py,sha256=
|
|
102
|
+
ocrd_utils/os.py,sha256=Fmy-Q4OMGlbHLMH9jhPRAxRN5curyEXrYoWJxztIl1w,13756
|
|
95
103
|
ocrd_utils/str.py,sha256=4P0MdX0LCTqDTnsi_y5wNOBXW_TuTFANF7NYRXjo4x0,10136
|
|
96
104
|
ocrd_validators/__init__.py,sha256=ZFc-UqRVBk9o1YesZFmr9lOepttNJ_NKx1Zdb7g_YsU,972
|
|
97
105
|
ocrd_validators/bagit-profile.yml,sha256=sdQJlSi7TOn1E9WYMOZ1shewJ-i_nPaKmsAFkh28TGY,1011
|
|
@@ -101,7 +109,7 @@ ocrd_validators/message_processing.schema.yml,sha256=HL7o96-7ejslVMXcp16sbo5IjfU
|
|
|
101
109
|
ocrd_validators/message_result.schema.yml,sha256=G6vt_JgIU7OGSaHj-2Jna6KWQ3bFWol5tnBArWEiVjM,681
|
|
102
110
|
ocrd_validators/mets.xsd,sha256=0Wrs9bObn0n-yEEIWyguIcUUuuP6KMEjD4I_p1_UlwY,138290
|
|
103
111
|
ocrd_validators/ocrd_network_message_validator.py,sha256=oafNWOjieBmTHFfYeCtyFFpW1gI0lDT6ycRr5Kvmfq0,561
|
|
104
|
-
ocrd_validators/ocrd_tool.schema.yml,sha256=
|
|
112
|
+
ocrd_validators/ocrd_tool.schema.yml,sha256=fDNr-QdEOBtYbz8aHmjdOUirPBKr3vfLUDtC88gu75U,10231
|
|
105
113
|
ocrd_validators/ocrd_tool_validator.py,sha256=0DWuyyOSbdbrrQ5kEfWZv_qp5rSmLzmFMUKcPGfCBgM,749
|
|
106
114
|
ocrd_validators/ocrd_zip_validator.py,sha256=t-cYIZ5llZSQ2EspFzm0m-FajkLRfAFTISmXe27wMtA,3720
|
|
107
115
|
ocrd_validators/page.xsd,sha256=abQ8C3gRLPMFm8lH62aTCfvTIWI23TpgEDcaW9YCt7I,85770
|
|
@@ -115,9 +123,9 @@ ocrd_validators/xlink.xsd,sha256=8fW7YAMWXN2PbB_MMvj9H5ZeFoEBDzuYBtlGC8_6ijw,318
|
|
|
115
123
|
ocrd_validators/xsd_mets_validator.py,sha256=YgiuNtwNDtn3LuvdFFscnmsGREF_wQ4wtA76yE2Iljw,469
|
|
116
124
|
ocrd_validators/xsd_page_validator.py,sha256=ggt-nmaz-DDyAPwm3ZMVvtChuV2BJ2ZEEbWpePL9vTk,469
|
|
117
125
|
ocrd_validators/xsd_validator.py,sha256=ahJo_oVvTK_JB0Cu4CkMC8l_gbzsyW91AxGtelMjqrg,2115
|
|
118
|
-
ocrd-3.
|
|
119
|
-
ocrd-3.
|
|
120
|
-
ocrd-3.
|
|
121
|
-
ocrd-3.
|
|
122
|
-
ocrd-3.
|
|
123
|
-
ocrd-3.
|
|
126
|
+
ocrd-3.8.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
127
|
+
ocrd-3.8.0.dist-info/METADATA,sha256=ieqTzchmz7OXQV6vlize7XzWDzWEk5ypLXtn4VTduHY,11396
|
|
128
|
+
ocrd-3.8.0.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
|
|
129
|
+
ocrd-3.8.0.dist-info/entry_points.txt,sha256=CI-NoDR1BYmsuAsJmPAn4NrN9guzdedHGUbC8QSmdGs,266
|
|
130
|
+
ocrd-3.8.0.dist-info/top_level.txt,sha256=pUgiN42t4KXC5rvpi6V8atza31XP4SCznXpXlVlvomM,75
|
|
131
|
+
ocrd-3.8.0.dist-info/RECORD,,
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
[console_scripts]
|
|
2
2
|
ocrd = ocrd.cli:cli
|
|
3
|
+
ocrd-command = ocrd.processor.builtin.shell_processor:cli
|
|
3
4
|
ocrd-dummy = ocrd.processor.builtin.dummy_processor:cli
|
|
4
5
|
ocrd-filter = ocrd.processor.builtin.filter_processor:cli
|
|
6
|
+
ocrd-merge = ocrd.processor.builtin.merge_processor:cli
|
ocrd_models/ocrd_agent.py
CHANGED
|
@@ -43,7 +43,7 @@ class OcrdAgent():
|
|
|
43
43
|
self.otherrole = otherrole
|
|
44
44
|
self.notes = notes
|
|
45
45
|
|
|
46
|
-
def
|
|
46
|
+
def __repr__(self):
|
|
47
47
|
"""
|
|
48
48
|
String representation
|
|
49
49
|
"""
|
|
@@ -142,7 +142,7 @@ class OcrdAgent():
|
|
|
142
142
|
"""
|
|
143
143
|
el_notes = self._el.findall(TAG_METS_NOTE)
|
|
144
144
|
if el_notes is not None:
|
|
145
|
-
return [(note.attrib, note.text)
|
|
145
|
+
return [(dict(note.attrib), note.text)
|
|
146
146
|
for note in el_notes]
|
|
147
147
|
|
|
148
148
|
@notes.setter
|
|
@@ -190,7 +190,7 @@ class ClientSideOcrdAgent():
|
|
|
190
190
|
self.otherrole = otherrole
|
|
191
191
|
self.notes = notes
|
|
192
192
|
|
|
193
|
-
def
|
|
193
|
+
def __repr__(self):
|
|
194
194
|
props = ', '.join([
|
|
195
195
|
'='.join([k, getattr(self, k) if getattr(self, k) else '---'])
|
|
196
196
|
for k in ['type', 'othertype', 'role', 'otherrole', 'name']
|
ocrd_network/__init__.py
CHANGED
|
@@ -3,4 +3,5 @@ from .constants import JobState
|
|
|
3
3
|
from .processing_server import ProcessingServer
|
|
4
4
|
from .processing_worker import ProcessingWorker
|
|
5
5
|
from .param_validators import DatabaseParamType, ServerAddressParamType, QueueServerParamType
|
|
6
|
+
from .resource_manager_server import ResourceManagerServer
|
|
6
7
|
from .server_cache import CacheLockedPages, CacheProcessingRequests
|
ocrd_network/cli/__init__.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
from .client import client_cli
|
|
2
2
|
from .processing_server import processing_server_cli
|
|
3
3
|
from .processing_worker import processing_worker_cli
|
|
4
|
+
from .resmgr_server import resource_manager_server_cli
|
|
4
5
|
|
|
5
6
|
__all__ = [
|
|
6
7
|
'client_cli',
|
|
7
8
|
'processing_server_cli',
|
|
8
9
|
'processing_worker_cli',
|
|
10
|
+
'resource_manager_server_cli'
|
|
9
11
|
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from ocrd_network import ResourceManagerServer, ServerAddressParamType
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@click.command('resmgr-server')
|
|
6
|
+
@click.option('-a', '--address',
|
|
7
|
+
help='The URL of the OCR-D resource manager server, format: host:port',
|
|
8
|
+
type=ServerAddressParamType(),
|
|
9
|
+
required=True)
|
|
10
|
+
def resource_manager_server_cli(address: str):
|
|
11
|
+
"""
|
|
12
|
+
Start standalone REST API OCR-D Resource Manager Server
|
|
13
|
+
"""
|
|
14
|
+
try:
|
|
15
|
+
# Note, the address is already validated with the type field
|
|
16
|
+
host, port = address.split(':')
|
|
17
|
+
resource_manager_server = ResourceManagerServer(
|
|
18
|
+
host = host,
|
|
19
|
+
port = int(port)
|
|
20
|
+
)
|
|
21
|
+
resource_manager_server.start()
|
|
22
|
+
except Exception as e:
|
|
23
|
+
raise Exception("OCR-D Resource Manager Server has failed with error") from e
|
ocrd_network/constants.py
CHANGED
|
@@ -10,6 +10,8 @@ OCRD_ALL_TOOL_JSON = "ocrd-all-tool.json"
|
|
|
10
10
|
# Used as a placeholder to lock all pages when no page_id is specified
|
|
11
11
|
SERVER_ALL_PAGES_PLACEHOLDER = "all_pages"
|
|
12
12
|
|
|
13
|
+
# TODO: Make this more configurable
|
|
14
|
+
RESOURCE_MANAGER_SERVER_PORT = 45555
|
|
13
15
|
|
|
14
16
|
class StrEnum(str, Enum):
|
|
15
17
|
def __str__(self):
|
|
@@ -48,6 +50,7 @@ class NetworkLoggingDirs(StrEnum):
|
|
|
48
50
|
PROCESSING_JOBS = "processing_jobs"
|
|
49
51
|
PROCESSING_SERVERS = "processing_servers"
|
|
50
52
|
PROCESSING_WORKERS = "processing_workers"
|
|
53
|
+
RESOURCE_MANAGER_SERVERS = "resource_manager_servers"
|
|
51
54
|
|
|
52
55
|
|
|
53
56
|
class ServerApiTags(StrEnum):
|
ocrd_network/logging_utils.py
CHANGED
|
@@ -56,3 +56,8 @@ def get_processing_server_logging_file_path(pid: int) -> Path:
|
|
|
56
56
|
def get_processing_worker_logging_file_path(processor_name: str, pid: int) -> Path:
|
|
57
57
|
log_file: str = f"worker.{pid}.{processor_name}.log"
|
|
58
58
|
return Path(get_root_logging_dir(NetworkLoggingDirs.PROCESSING_WORKERS), log_file)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_resource_manager_server_logging_file_path(pid: int) -> Path:
|
|
62
|
+
log_file: str = f"resource_manager_server.{pid}.log"
|
|
63
|
+
return Path(get_root_logging_dir(NetworkLoggingDirs.RESOURCE_MANAGER_SERVERS), log_file)
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from os import getpid
|
|
3
|
+
from shutil import which
|
|
4
|
+
from typing import Any
|
|
5
|
+
from uvicorn import run as uvicorn_run
|
|
6
|
+
from fastapi import APIRouter, FastAPI, HTTPException, status
|
|
7
|
+
|
|
8
|
+
from ocrd import OcrdResourceManager
|
|
9
|
+
from ocrd_utils import getLogger, get_ocrd_tool_json, initLogging
|
|
10
|
+
from .logging_utils import configure_file_handler_with_formatter, get_resource_manager_server_logging_file_path
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ResourceManagerServer(FastAPI):
|
|
14
|
+
def __init__(self, host: str, port: int) -> None:
|
|
15
|
+
self.title = f"OCR-D Resource Manager Server"
|
|
16
|
+
super().__init__(
|
|
17
|
+
title=self.title,
|
|
18
|
+
on_startup=[self.on_startup],
|
|
19
|
+
on_shutdown=[self.on_shutdown],
|
|
20
|
+
description=self.title
|
|
21
|
+
)
|
|
22
|
+
initLogging()
|
|
23
|
+
self.log = getLogger("ocrd_network.resource_manager_server")
|
|
24
|
+
log_file = get_resource_manager_server_logging_file_path(pid=getpid())
|
|
25
|
+
configure_file_handler_with_formatter(self.log, log_file=log_file, mode="a")
|
|
26
|
+
|
|
27
|
+
self.resmgr_instance = OcrdResourceManager()
|
|
28
|
+
|
|
29
|
+
self.hostname = host
|
|
30
|
+
self.port = port
|
|
31
|
+
|
|
32
|
+
self.add_api_routes()
|
|
33
|
+
|
|
34
|
+
def start(self):
|
|
35
|
+
uvicorn_run(self, host=self.hostname, port=int(self.port))
|
|
36
|
+
|
|
37
|
+
async def on_startup(self):
|
|
38
|
+
self.log.info(f"Starting {self.title}")
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
async def on_shutdown(self) -> None:
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
def add_api_routes(self):
|
|
45
|
+
base_router = APIRouter()
|
|
46
|
+
base_router.add_api_route(
|
|
47
|
+
path="/",
|
|
48
|
+
endpoint=self.home_page,
|
|
49
|
+
methods=["GET"],
|
|
50
|
+
status_code=status.HTTP_200_OK,
|
|
51
|
+
summary="Get information about the OCR-D Resource Manager Server"
|
|
52
|
+
)
|
|
53
|
+
base_router.add_api_route(
|
|
54
|
+
path="/list_available",
|
|
55
|
+
endpoint=self.list_available_resources,
|
|
56
|
+
methods=["GET"],
|
|
57
|
+
status_code=status.HTTP_200_OK,
|
|
58
|
+
summary=""
|
|
59
|
+
)
|
|
60
|
+
base_router.add_api_route(
|
|
61
|
+
path="/list_installed",
|
|
62
|
+
endpoint=self.list_installed_resources,
|
|
63
|
+
methods=["GET"],
|
|
64
|
+
status_code=status.HTTP_200_OK,
|
|
65
|
+
summary=""
|
|
66
|
+
)
|
|
67
|
+
base_router.add_api_route(
|
|
68
|
+
path="/download",
|
|
69
|
+
endpoint=self.download_resource,
|
|
70
|
+
methods=["GET"],
|
|
71
|
+
status_code=status.HTTP_200_OK,
|
|
72
|
+
summary=""
|
|
73
|
+
)
|
|
74
|
+
self.include_router(base_router)
|
|
75
|
+
|
|
76
|
+
async def home_page(self):
|
|
77
|
+
message = f"The home page of the {self.title}"
|
|
78
|
+
json_message = {
|
|
79
|
+
"message": message,
|
|
80
|
+
"time": datetime.now().strftime("%Y-%m-%d %H:%M")
|
|
81
|
+
}
|
|
82
|
+
return json_message
|
|
83
|
+
|
|
84
|
+
async def list_available_resources(
|
|
85
|
+
self,
|
|
86
|
+
executable: Any = "ocrd-dummy",
|
|
87
|
+
dynamic: bool = True,
|
|
88
|
+
name: Any = None,
|
|
89
|
+
database: Any = None,
|
|
90
|
+
url: Any = None
|
|
91
|
+
):
|
|
92
|
+
if executable == '*':
|
|
93
|
+
message = f"'*' is not an acceptable executable name! Try with a specific executable."
|
|
94
|
+
self.log.error(message)
|
|
95
|
+
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=message)
|
|
96
|
+
result = self.resmgr_instance.list_available(executable, dynamic, name, database, url)
|
|
97
|
+
json_message = {
|
|
98
|
+
"result": result
|
|
99
|
+
}
|
|
100
|
+
return json_message
|
|
101
|
+
|
|
102
|
+
async def list_installed_resources(self, executable: Any = None):
|
|
103
|
+
if executable == '*':
|
|
104
|
+
message = f"'*' is not an acceptable executable name! Try with a specific executable."
|
|
105
|
+
self.log.error(message)
|
|
106
|
+
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=message)
|
|
107
|
+
result = self.resmgr_instance.list_available(executable)
|
|
108
|
+
json_message = {
|
|
109
|
+
"result": result
|
|
110
|
+
}
|
|
111
|
+
return json_message
|
|
112
|
+
|
|
113
|
+
async def download_resource(
|
|
114
|
+
self,
|
|
115
|
+
executable: str = "ocrd-dummy",
|
|
116
|
+
name: Any = None,
|
|
117
|
+
location: Any = None,
|
|
118
|
+
any_url: str = '',
|
|
119
|
+
no_dynamic: bool = False,
|
|
120
|
+
resource_type: str = 'file',
|
|
121
|
+
path_in_archive: str = '.',
|
|
122
|
+
allow_uninstalled: bool = True,
|
|
123
|
+
overwrite: bool = True
|
|
124
|
+
):
|
|
125
|
+
resmgr = OcrdResourceManager()
|
|
126
|
+
response = []
|
|
127
|
+
if executable == '*':
|
|
128
|
+
message = f"'*' is not an acceptable executable name! Try with a specific executable."
|
|
129
|
+
self.log.error(message)
|
|
130
|
+
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=message)
|
|
131
|
+
if name == '*':
|
|
132
|
+
name = None
|
|
133
|
+
if executable and not which(executable):
|
|
134
|
+
if not allow_uninstalled:
|
|
135
|
+
message = (f"Executable '{executable}' is not installed. To download resources anyway, "
|
|
136
|
+
f"use the -a/--allow-uninstalled flag")
|
|
137
|
+
self.log.error(message)
|
|
138
|
+
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=message)
|
|
139
|
+
else:
|
|
140
|
+
message = f"Executable '{executable}' is not installed, but downloading resources anyway."
|
|
141
|
+
self.log.info(message)
|
|
142
|
+
response.append(message)
|
|
143
|
+
reslist = resmgr.list_available(executable=executable, dynamic=not no_dynamic, name=name)
|
|
144
|
+
if not any(r[1] for r in reslist):
|
|
145
|
+
message = f"No resources {name} found in registry for executable {executable}"
|
|
146
|
+
self.log.info(message)
|
|
147
|
+
response.append(message)
|
|
148
|
+
if executable and name:
|
|
149
|
+
reslist = [(executable, [{
|
|
150
|
+
'url': any_url or '???',
|
|
151
|
+
'name': name,
|
|
152
|
+
'type': resource_type,
|
|
153
|
+
'path_in_archive': path_in_archive}]
|
|
154
|
+
)]
|
|
155
|
+
for this_executable, this_reslist in reslist:
|
|
156
|
+
resource_locations = get_ocrd_tool_json(this_executable)['resource_locations']
|
|
157
|
+
if not location:
|
|
158
|
+
location = resource_locations[0]
|
|
159
|
+
elif location not in resource_locations:
|
|
160
|
+
response.append(
|
|
161
|
+
f"The selected --location {location} is not in the {this_executable}'s resource search path, "
|
|
162
|
+
f"refusing to install to invalid location. Instead installing to: {resource_locations[0]}")
|
|
163
|
+
res_dest_dir = resmgr.build_resource_dest_dir(location=location, executable=this_executable)
|
|
164
|
+
for res_dict in this_reslist:
|
|
165
|
+
try:
|
|
166
|
+
fpath = resmgr.handle_resource(
|
|
167
|
+
res_dict=res_dict,
|
|
168
|
+
executable=this_executable,
|
|
169
|
+
dest_dir=res_dest_dir,
|
|
170
|
+
any_url=any_url,
|
|
171
|
+
overwrite=overwrite,
|
|
172
|
+
resource_type=resource_type,
|
|
173
|
+
path_in_archive=path_in_archive
|
|
174
|
+
)
|
|
175
|
+
if not fpath:
|
|
176
|
+
continue
|
|
177
|
+
except FileExistsError as exc:
|
|
178
|
+
response.append(str(exc))
|
|
179
|
+
usage = res_dict.get('parameter_usage', 'as-is')
|
|
180
|
+
response.append(f"Use in parameters as '{resmgr.parameter_usage(res_dict['name'], usage)}'")
|
|
181
|
+
json_message = { "result": response }
|
|
182
|
+
return json_message
|
|
@@ -36,7 +36,7 @@ class CustomDockerClient(DockerClient):
|
|
|
36
36
|
raise ValueError("Both 'password' and 'keypath' provided - one must be provided")
|
|
37
37
|
if ("password" not in kwargs) and ("keypath" not in kwargs):
|
|
38
38
|
raise ValueError("Missing 'password' or 'keypath' - one must be provided")
|
|
39
|
-
self.api = APIClient(base_url=f"ssh://{host}", use_ssh_client=True, version="
|
|
39
|
+
self.api = APIClient(base_url=f"ssh://{host}", use_ssh_client=True, version="auto")
|
|
40
40
|
self.api.mount(
|
|
41
41
|
prefix="http+docker://ssh", adapter=self.CustomSshHttpAdapter(base_url=f"ssh://{user}@{host}:22", **kwargs)
|
|
42
42
|
)
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
from logging import Logger
|
|
2
|
-
from
|
|
3
|
-
from typing import Dict, List
|
|
2
|
+
from typing import Dict, List, Optional
|
|
4
3
|
|
|
5
|
-
from
|
|
6
|
-
from
|
|
4
|
+
from docker import APIClient
|
|
5
|
+
from paramiko import SSHClient
|
|
6
|
+
|
|
7
|
+
from ..constants import RESOURCE_MANAGER_SERVER_PORT
|
|
8
|
+
from .connection_clients import CustomDockerClient, create_docker_client, create_ssh_client
|
|
9
|
+
from .network_agents import (
|
|
10
|
+
DataProcessingWorker, DeployType, deploy_agent_native_get_pid_hack)
|
|
7
11
|
|
|
8
12
|
|
|
9
13
|
class DataHost:
|
|
@@ -11,6 +15,8 @@ class DataHost:
|
|
|
11
15
|
self, host: str, username: str, password: str, keypath: str, workers: List[Dict], servers: List[Dict]
|
|
12
16
|
) -> None:
|
|
13
17
|
self.host = host
|
|
18
|
+
self.resource_manager_port = RESOURCE_MANAGER_SERVER_PORT
|
|
19
|
+
self.resource_manager_pid = None
|
|
14
20
|
self.username = username
|
|
15
21
|
self.password = password
|
|
16
22
|
self.keypath = keypath
|
|
@@ -22,14 +28,11 @@ class DataHost:
|
|
|
22
28
|
|
|
23
29
|
# Connection clients, ssh for native deployment, docker for docker deployment
|
|
24
30
|
self.ssh_client = None
|
|
25
|
-
self.docker_client = None
|
|
26
|
-
|
|
27
|
-
# Time to wait between deploying single workers
|
|
28
|
-
self.wait_between_deploys: float = 0.3
|
|
31
|
+
self.docker_client: Optional[CustomDockerClient] = None
|
|
29
32
|
|
|
30
|
-
# Lists of
|
|
31
|
-
self.workers_native = []
|
|
32
|
-
self.workers_docker = []
|
|
33
|
+
# Lists of network agents based on their agent and deployment type
|
|
34
|
+
self.workers_native: List[DataProcessingWorker] = []
|
|
35
|
+
self.workers_docker: List[DataProcessingWorker] = []
|
|
33
36
|
|
|
34
37
|
if not workers:
|
|
35
38
|
workers = []
|
|
@@ -68,6 +71,13 @@ class DataHost:
|
|
|
68
71
|
self.docker_client = create_docker_client(self.host, self.username, self.password, self.keypath)
|
|
69
72
|
return self.docker_client
|
|
70
73
|
|
|
74
|
+
def __deploy_network_agent_resource_manager_server(self, logger: Logger):
|
|
75
|
+
logger.info(f"Deploying resource manager server on host: {self.host}:{self.resource_manager_port}")
|
|
76
|
+
start_cmd = f"ocrd network resmgr-server --address {self.host}:{self.resource_manager_port} &"
|
|
77
|
+
pid = deploy_agent_native_get_pid_hack(logger, self.ssh_client, start_cmd)
|
|
78
|
+
logger.info(f"Deployed: OCR-D Resource Manager Server [{pid}]: {self.host}:{self.resource_manager_port}")
|
|
79
|
+
self.resource_manager_pid = pid
|
|
80
|
+
|
|
71
81
|
def __deploy_single_worker(
|
|
72
82
|
self, logger: Logger, worker_data: DataProcessingWorker,
|
|
73
83
|
mongodb_url: str, rabbitmq_url: str
|
|
@@ -86,7 +96,6 @@ class DataHost:
|
|
|
86
96
|
connection_client = self.docker_client
|
|
87
97
|
|
|
88
98
|
worker_data.deploy_network_agent(logger, connection_client, mongodb_url, rabbitmq_url)
|
|
89
|
-
sleep(self.wait_between_deploys)
|
|
90
99
|
|
|
91
100
|
def __deploy_all_workers(self, logger: Logger, mongodb_url: str, rabbitmq_url: str):
|
|
92
101
|
logger.info(f"Deploying processing workers on host: {self.host}")
|
|
@@ -95,17 +104,24 @@ class DataHost:
|
|
|
95
104
|
logger.info("No processing workers found to be deployed")
|
|
96
105
|
for data_worker in self.workers_native:
|
|
97
106
|
self.__deploy_single_worker(logger, data_worker, mongodb_url, rabbitmq_url)
|
|
107
|
+
logger.info(f"Deployed: {data_worker}")
|
|
98
108
|
for data_worker in self.workers_docker:
|
|
99
109
|
self.__deploy_single_worker(logger, data_worker, mongodb_url, rabbitmq_url)
|
|
110
|
+
logger.info(f"Deployed: {data_worker}")
|
|
100
111
|
|
|
101
112
|
def deploy_workers(self, logger: Logger, mongodb_url: str, rabbitmq_url: str) -> None:
|
|
102
113
|
if self.needs_ssh_connector and not self.ssh_client:
|
|
103
114
|
logger.debug("Creating missing ssh connector before deploying")
|
|
104
|
-
|
|
115
|
+
client = self.create_connection_client(client_type="ssh")
|
|
116
|
+
assert isinstance(client, SSHClient)
|
|
117
|
+
self.ssh_client = client
|
|
105
118
|
if self.needs_docker_connector:
|
|
106
119
|
logger.debug("Creating missing docker connector before deploying")
|
|
107
|
-
|
|
120
|
+
client = self.create_connection_client(client_type="docker")
|
|
121
|
+
assert isinstance(client, CustomDockerClient)
|
|
122
|
+
self.docker_client = client
|
|
108
123
|
|
|
124
|
+
self.__deploy_network_agent_resource_manager_server(logger)
|
|
109
125
|
self.__deploy_all_workers(logger=logger, mongodb_url=mongodb_url, rabbitmq_url=rabbitmq_url)
|
|
110
126
|
|
|
111
127
|
if self.ssh_client:
|
|
@@ -115,6 +131,12 @@ class DataHost:
|
|
|
115
131
|
self.docker_client.close()
|
|
116
132
|
self.docker_client = None
|
|
117
133
|
|
|
134
|
+
def __stop_network_agent_resource_manager_server(self, logger: Logger):
|
|
135
|
+
logger.info(f"Stopping OCR-D Resource Manager Server [{self.resource_manager_pid}]: "
|
|
136
|
+
f"{self.host}:{self.resource_manager_port}")
|
|
137
|
+
assert self.ssh_client, "SSH client connection missing"
|
|
138
|
+
self.ssh_client.exec_command(f"kill {self.resource_manager_pid}")
|
|
139
|
+
|
|
118
140
|
def __stop_worker(self, logger: Logger, name: str, deploy_type: DeployType, pid: str):
|
|
119
141
|
worker_info = f"Processing Worker: deploy: {deploy_type}, name: {name}"
|
|
120
142
|
if not pid:
|
|
@@ -132,10 +154,15 @@ class DataHost:
|
|
|
132
154
|
def stop_workers(self, logger: Logger):
|
|
133
155
|
if self.needs_ssh_connector and not self.ssh_client:
|
|
134
156
|
logger.debug("Creating missing ssh connector before stopping")
|
|
135
|
-
|
|
157
|
+
client = self.create_connection_client(client_type="ssh")
|
|
158
|
+
assert isinstance(client, SSHClient)
|
|
159
|
+
self.ssh_client = client
|
|
136
160
|
if self.needs_docker_connector and not self.docker_client:
|
|
137
161
|
logger.debug("Creating missing docker connector before stopping")
|
|
138
|
-
|
|
162
|
+
client = self.create_connection_client(client_type="docker")
|
|
163
|
+
assert isinstance(client, CustomDockerClient)
|
|
164
|
+
self.docker_client = client
|
|
165
|
+
self.__stop_network_agent_resource_manager_server(logger=logger)
|
|
139
166
|
|
|
140
167
|
logger.info(f"Stopping processing workers on host: {self.host}")
|
|
141
168
|
amount_workers = len(self.workers_native) + len(self.workers_docker)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from logging import Logger
|
|
2
|
+
from time import sleep
|
|
2
3
|
from typing import Any
|
|
3
4
|
|
|
4
5
|
from re import search as re_search
|
|
@@ -25,7 +26,8 @@ def deploy_agent_native_get_pid_hack(logger: Logger, ssh_client, start_cmd: str)
|
|
|
25
26
|
output = stdout.read().decode("utf-8")
|
|
26
27
|
stdout.close()
|
|
27
28
|
stdin.close()
|
|
28
|
-
|
|
29
|
+
pid = re_search(r"xyz([0-9]+)xyz", output).group(1) # type: ignore
|
|
30
|
+
return pid
|
|
29
31
|
|
|
30
32
|
|
|
31
33
|
# TODO: Implement the actual method that is missing
|
|
@@ -51,6 +53,12 @@ class DataNetworkAgent:
|
|
|
51
53
|
# The id is assigned when the agent is deployed
|
|
52
54
|
self.pid = pid
|
|
53
55
|
|
|
56
|
+
# Time to wait between deploying agents
|
|
57
|
+
self.wait_between_agent_deploys: float = 0.3
|
|
58
|
+
|
|
59
|
+
def __str__(self):
|
|
60
|
+
return f"{self.pid} {self.deploy_type} {self.processor_name} on host: {self.host}"
|
|
61
|
+
|
|
54
62
|
def _start_native_instance(self, logger: Logger, ssh_client, start_cmd: str):
|
|
55
63
|
if self.deploy_type != DeployType.NATIVE:
|
|
56
64
|
raise RuntimeError(f"Mismatch of deploy type when starting network agent: {self.processor_name}")
|
|
@@ -76,11 +84,17 @@ class DataProcessingWorker(DataNetworkAgent):
|
|
|
76
84
|
def deploy_network_agent(self, logger: Logger, connector_client, database_url: str, queue_url: str):
|
|
77
85
|
if self.deploy_type == DeployType.NATIVE:
|
|
78
86
|
start_cmd = f"{self.processor_name} --database {database_url} --queue {queue_url} &"
|
|
87
|
+
assert connector_client, f"SSH client connection missing."
|
|
79
88
|
self.pid = self._start_native_instance(logger, connector_client, start_cmd)
|
|
89
|
+
sleep(self.wait_between_agent_deploys)
|
|
80
90
|
return self.pid
|
|
81
91
|
if self.deploy_type == DeployType.DOCKER:
|
|
82
92
|
# TODO: add real command to start processing worker in docker here
|
|
83
93
|
start_cmd = ""
|
|
94
|
+
assert connector_client, f"Docker client connection missing."
|
|
95
|
+
if not start_cmd:
|
|
96
|
+
raise RuntimeError("Missing start command for the Processing Worker in docker mode")
|
|
84
97
|
self.pid = self._start_docker_instance(logger, connector_client, start_cmd)
|
|
98
|
+
sleep(self.wait_between_agent_deploys)
|
|
85
99
|
return self.pid
|
|
86
100
|
raise RuntimeError(f"Unknown deploy type of {self.__dict__}")
|
ocrd_utils/__init__.py
CHANGED
|
@@ -70,7 +70,8 @@ Utility functions and constants usable in various circumstances.
|
|
|
70
70
|
|
|
71
71
|
filesystem-related utilities
|
|
72
72
|
|
|
73
|
-
* :py:func:`
|
|
73
|
+
* :py:func:`is_git_url`,
|
|
74
|
+
:py:func:`is_string`,
|
|
74
75
|
:py:func:`membername`,
|
|
75
76
|
:py:func:`concat_padded`,
|
|
76
77
|
:py:func:`nth_url_segment`,
|
|
@@ -118,6 +119,7 @@ from .constants import (
|
|
|
118
119
|
REGEX_PREFIX,
|
|
119
120
|
REGEX_FILE_ID,
|
|
120
121
|
RESOURCE_LOCATIONS,
|
|
122
|
+
RESOURCE_TYPES,
|
|
121
123
|
LOG_FORMAT,
|
|
122
124
|
LOG_TIMEFMT,
|
|
123
125
|
VERSION,
|
|
@@ -184,9 +186,11 @@ from .os import (
|
|
|
184
186
|
get_processor_resource_types,
|
|
185
187
|
get_ocrd_tool_json,
|
|
186
188
|
get_moduledir,
|
|
189
|
+
get_env_locations,
|
|
187
190
|
guess_media_type,
|
|
188
191
|
list_all_resources,
|
|
189
192
|
is_file_in_directory,
|
|
193
|
+
is_git_url,
|
|
190
194
|
list_resource_candidates,
|
|
191
195
|
atomic_write,
|
|
192
196
|
pushd_popd,
|