2
2
This module contains the necessary tools to discover and load
3
3
the handlers for serving HTTPS
4
4
"""
5
-
6
- from __future__ import absolute_import
7
- from __future__ import division
8
- from __future__ import print_function
9
-
10
- __RCSID__ = "$Id$"
11
-
12
- import inspect
13
- from tornado .web import url as TornadoURL , RequestHandler
5
+ from tornado .web import RequestHandler
14
6
15
7
from DIRAC import gConfig , gLogger , S_ERROR , S_OK
16
8
from DIRAC .ConfigurationSystem .Client import PathFinder
17
9
from DIRAC .Core .Base .private .ModuleLoader import ModuleLoader
18
- from DIRAC .Core .Utilities .ObjectLoader import ObjectLoader
19
-
20
-
21
- def urlFinder (module ):
22
- """
23
- Tries to guess the url from module name.
24
- The URL would be of the form ``/System/Component`` (e.g. ``DataManagement/FileCatalog``)
25
- We search something which looks like ``<...>.<component>System.<...>.<service>Handler``
26
-
27
- :param module: full module name (e.g. "DIRAC.something.something")
28
-
29
- :returns: the deduced URL or None
30
- """
31
- sections = module .split ("." )
32
- for section in sections :
33
- # This condition is a bit long
34
- # We search something which look like <...>.<component>System.<...>.<service>Handler
35
- # If find we return /<component>/<service>
36
- if section .endswith ("System" ) and sections [- 1 ].endswith ("Handler" ):
37
- return "/" .join (["" , section [: - len ("System" )], sections [- 1 ][: - len ("Handler" )]])
38
10
39
11
40
12
class HandlerManager (object ):
@@ -46,7 +18,7 @@ class HandlerManager(object):
46
18
47
19
Each of the Handler will have one associated route to it:
48
20
49
- * Directly specified as ``LOCATION `` in the handler module
21
+ * Directly specified as ``DEFAULT_LOCATION `` in the handler module
50
22
* automatically deduced from the module name, of the form
51
23
``System/Component`` (e.g. ``DataManagement/FileCatalog``)
52
24
"""
@@ -64,59 +36,8 @@ def __init__(self, services, endpoints):
64
36
If ``True``, loads all endpoints from CS
65
37
:type endpoints: bool or list
66
38
"""
67
- self .loader = None
68
39
self .__handlers = {}
69
- self .__services = services
70
- self .__endpoints = endpoints
71
- self .__objectLoader = ObjectLoader ()
72
-
73
- def __addHandler (self , handlerPath , handler , urls = None , port = None ):
74
- """
75
- Function which add handler to list of known handlers
76
-
77
- :param str handlerPath: module name, e.g.: `Framework/Auth`
78
- :param object handler: handler class
79
- :param list urls: request path
80
- :param int port: port
81
-
82
- :return: S_OK()/S_ERROR()
83
- """
84
- # First of all check if we can find route
85
- # If urls is not given, try to discover it
86
- if urls is None :
87
- # FIRST TRY: Url is hardcoded
88
- try :
89
- urls = handler .LOCATION
90
- # SECOND TRY: URL can be deduced from path
91
- except AttributeError :
92
- gLogger .debug ("No location defined for %s try to get it from path" % handlerPath )
93
- urls = urlFinder (handlerPath )
94
-
95
- if not urls :
96
- gLogger .warn ("URL not found for %s" % (handlerPath ))
97
- return S_ERROR ("URL not found for %s" % (handlerPath ))
98
-
99
- for url in urls if isinstance (urls , (list , tuple )) else [urls ]:
100
- # We add "/" if missing at begin, e.g. we found "Framework/Service"
101
- # URL can't be relative in Tornado
102
- if url and not url .startswith ("/" ):
103
- url = "/%s" % url
104
-
105
- # Some new handler
106
- if handlerPath not in self .__handlers :
107
- gLogger .debug ("Add new handler %s with port %s" % (handlerPath , port ))
108
- self .__handlers [handlerPath ] = {"URLs" : [], "Port" : port }
109
-
110
- # Check if URL already loaded
111
- if (url , handler ) in self .__handlers [handlerPath ]["URLs" ]:
112
- gLogger .debug ("URL: %s already loaded for %s " % (url , handlerPath ))
113
- continue
114
-
115
- # Finally add the URL to handlers
116
- gLogger .info ("Add new URL %s to %s handler" % (url , handlerPath ))
117
- self .__handlers [handlerPath ]["URLs" ].append ((url , handler ))
118
-
119
- return S_OK ()
40
+ self .instances = dict (Service = services , API = endpoints )
120
41
121
42
def discoverHandlers (self , handlerInstance ):
122
43
"""
@@ -160,130 +81,91 @@ def discoverHandlers(self, handlerInstance):
160
81
pass
161
82
return urls
162
83
163
- def loadServicesHandlers (self , services = None ):
164
- """
165
- Load a list of handler from list of service using DIRAC moduleLoader
84
+ def __load (self , instances , componentType , pathFinder ):
85
+ """Load a list of handler from list of given instances using DIRAC moduleLoader
166
86
Use :py:class:`DIRAC.Core.Base.private.ModuleLoader`
167
87
88
+ :return: S_OK()/S_ERROR()
89
+ """
90
+ # list of instances, e.g. ['Framework/Hello', 'Configuration/Server']
91
+ if isinstance (instances , str ):
92
+ # make sure we have a list
93
+ instances = [instances ]
94
+
95
+ instances = self .instances [componentType ] if instances is None else instances if instances else []
96
+
97
+ # `True` means automatically view the configuration
98
+ if instances is True :
99
+ instances = self .discoverHandlers (f"{ componentType } s" )
100
+ if not instances :
101
+ return S_OK ()
102
+
103
+ # Extract ports, e.g.: ['Framework/MyService', 'Framework/MyService2:9443]
104
+ port , instances = self .__extractPorts (instances )
105
+
106
+ loader = ModuleLoader (componentType , pathFinder , RequestHandler , moduleSuffix = "Handler" )
107
+
108
+ # Use DIRAC system to load: search in CS if path is given and if not defined
109
+ # it search in place it should be (e.g. in DIRAC/FrameworkSystem/< component type >)
110
+ result = loader .loadModules (instances )
111
+ if result ["OK" ]:
112
+ for module in loader .getModules ().values ():
113
+ handler = module ["classObj" ]
114
+ fullComponentName = module ["modName" ]
115
+
116
+ # Define the system and component name as the attributes of the handler that belongs to them
117
+ handler .SYSTEM_NAME , handler .COMPONENT_NAME = fullComponentName .split ("/" )
118
+
119
+ gLogger .info (f"Found new handler { fullComponentName } " )
120
+
121
+ # at this stage we run the basic handler initialization
122
+ # see DIRAC.Core.Tornado.Server.private.BaseRequestHandler for more details
123
+ # this method should return a list of routes associated with the handler, it is a regular expressions
124
+ # see https://www.tornadoweb.org/en/stable/routing.html#tornado.routing.URLSpec, ``pattern`` argument.
125
+ urls = handler ._BaseRequestHandler__pre_initialize ()
126
+
127
+ # First of all check if we can find route
128
+ if not urls :
129
+ gLogger .warn (f"URL not found for { fullComponentName } " )
130
+ return S_ERROR (f"URL not found for { fullComponentName } " )
131
+
132
+ # Add new handler routes
133
+ self .__handlers [fullComponentName ] = dict (URLs = list (set (urls )), Port = port .get (fullComponentName ))
134
+
135
+ return result
136
+
137
+ def loadServicesHandlers (self , services = None ):
138
+ """Load services
139
+
168
140
:param services: List of service handlers to load. Default value set at initialization
169
141
If ``True``, loads all services from CS
170
142
:type services: bool or list
171
143
172
144
:return: S_OK()/S_ERROR()
173
145
"""
174
- # list of services, e.g. ['Framework/Hello', 'Configuration/Server']
175
- if isinstance (services , str ):
176
- services = [services ]
177
- # list of services
178
- self .__services = self .__services if services is None else services if services else []
179
-
180
- if self .__services is True :
181
- self .__services = self .discoverHandlers ("Services" )
182
-
183
- if self .__services :
184
- # Extract ports
185
- ports , self .__services = self .__extractPorts (self .__services )
186
-
187
- self .loader = ModuleLoader ("Service" , PathFinder .getServiceSection , RequestHandler , moduleSuffix = "Handler" )
188
-
189
- # Use DIRAC system to load: search in CS if path is given and if not defined
190
- # it search in place it should be (e.g. in DIRAC/FrameworkSystem/Service)
191
- load = self .loader .loadModules (self .__services )
192
- if not load ["OK" ]:
193
- return load
194
- for module in self .loader .getModules ().values ():
195
- url = module ["loadName" ]
196
-
197
- # URL can be like https://domain:port/service/name or just service/name
198
- # Here we just want the service name, for tornado
199
- serviceTuple = url .replace ("https://" , "" ).split ("/" )[- 2 :]
200
- url = "%s/%s" % (serviceTuple [0 ], serviceTuple [1 ])
201
- self .__addHandler (module ["loadName" ], module ["classObj" ], url , ports .get (module ["modName" ]))
202
- return S_OK ()
203
-
204
- def __extractPorts (self , serviceURIs ):
205
- """Extract ports from serviceURIs
206
-
207
- :param list serviceURIs: list of uri that can contain port, .e.g:: System/Service:port
208
-
209
- :return: (dict, list)
210
- """
211
- portMapping = {}
212
- newURLs = []
213
- for _url in serviceURIs :
214
- if ":" in _url :
215
- urlTuple = _url .split (":" )
216
- if urlTuple [0 ] not in portMapping :
217
- portMapping [urlTuple [0 ]] = urlTuple [1 ]
218
- newURLs .append (urlTuple [0 ])
219
- else :
220
- newURLs .append (_url )
221
- return (portMapping , newURLs )
146
+ return self .__load (services , "Service" , PathFinder .getServiceSection )
222
147
223
148
def loadEndpointsHandlers (self , endpoints = None ):
224
- """
225
- Load a list of handler from list of endpoints using DIRAC moduleLoader
226
- Use :py:class:`DIRAC.Core.Base.private.ModuleLoader`
149
+ """Load endpoints
227
150
228
151
:param endpoints: List of endpoint handlers to load. Default value set at initialization
229
152
If ``True``, loads all endpoints from CS
230
153
:type endpoints: bool or list
231
154
232
155
:return: S_OK()/S_ERROR()
233
156
"""
234
- # list of endpoints, e.g. ['Framework/Auth', ...]
235
- if isinstance (endpoints , str ):
236
- endpoints = [endpoints ]
237
- # list of endpoints. If __endpoints is ``True`` then list of endpoints will dicover from CS
238
- self .__endpoints = self .__endpoints if endpoints is None else endpoints if endpoints else []
239
-
240
- if self .__endpoints is True :
241
- self .__endpoints = self .discoverHandlers ("APIs" )
242
-
243
- if self .__endpoints :
244
- # Extract ports
245
- ports , self .__endpoints = self .__extractPorts (self .__endpoints )
246
-
247
- self .loader = ModuleLoader ("API" , PathFinder .getAPISection , RequestHandler , moduleSuffix = "Handler" )
248
-
249
- # Use DIRAC system to load: search in CS if path is given and if not defined
250
- # it search in place it should be (e.g. in DIRAC/FrameworkSystem/API)
251
- load = self .loader .loadModules (self .__endpoints )
252
- if not load ["OK" ]:
253
- return load
254
- for module in self .loader .getModules ().values ():
255
- handler = module ["classObj" ]
256
- if not handler .LOCATION :
257
- handler .LOCATION = urlFinder (module ["loadName" ])
258
- urls = []
259
- # Look for methods that are exported
260
- for mName , mObj in inspect .getmembers (handler ):
261
- if inspect .isroutine (mObj ) and mName .find (handler .METHOD_PREFIX ) == 0 :
262
- methodName = mName [len (handler .METHOD_PREFIX ) :]
263
- args = getattr (handler , "path_%s" % methodName , [])
264
- gLogger .debug (
265
- " - Route %s/%s -> %s %s" % (handler .LOCATION , methodName , module ["loadName" ], mName )
266
- )
267
- url = "%s%s" % (handler .LOCATION , "" if methodName == "index" else ("/%s" % methodName ))
268
- if args :
269
- url += r"[\/]?%s" % "/" .join (args )
270
- urls .append (url )
271
- gLogger .debug (" * %s" % url )
272
- self .__addHandler (module ["loadName" ], handler , urls , ports .get (module ["modName" ]))
273
- return S_OK ()
157
+ return self .__load (endpoints , "API" , PathFinder .getAPISection )
274
158
275
159
def getHandlersURLs (self ):
276
160
"""
277
161
Get all handler for usage in Tornado, as a list of tornado.web.url
278
- If there is no handler found before, it try to find them
279
162
280
163
:returns: a list of URL (not the string with "https://..." but the tornado object)
281
164
see http://www.tornadoweb.org/en/stable/web.html#tornado.web.URLSpec
282
165
"""
283
166
urls = []
284
- for handlerData in self .__handlers .values ():
285
- for url in handlerData ["URLs" ]:
286
- urls .append (TornadoURL (* url ))
167
+ for handler in self .__handlers :
168
+ urls += self .__handlers [handler ]["URLs" ]
287
169
return urls
288
170
289
171
def getHandlersDict (self ):
@@ -295,3 +177,22 @@ def getHandlersDict(self):
295
177
and port as value for 'Port' key
296
178
"""
297
179
return self .__handlers
180
+
181
+ def __extractPorts (self , serviceURIs : list ) -> tuple :
182
+ """Extract ports from serviceURIs
183
+
184
+ :param list serviceURIs: list of uri that can contain port, .e.g:: System/Service:port
185
+
186
+ :return: (dict, list)
187
+ """
188
+ portMapping = {}
189
+ newURLs = []
190
+ for _url in serviceURIs :
191
+ if ":" in _url :
192
+ urlTuple = _url .split (":" )
193
+ if urlTuple [0 ] not in portMapping :
194
+ portMapping [urlTuple [0 ]] = urlTuple [1 ]
195
+ newURLs .append (urlTuple [0 ])
196
+ else :
197
+ newURLs .append (_url )
198
+ return (portMapping , newURLs )
0 commit comments