defsetup(name, host='0.0.0.0'): # nosec """Setup OSprofiler notifier and enable profiling. :param name: name of the service that will be profiled :param host: hostname or host IP address that the service will be running on. By default host will be set to 0.0.0.0, but more specified host name / address usage is highly recommended. """ if CONF.profiler.enabled: osprofiler.initializer.init_from_conf( conf=CONF, context={}, project="keystone", service=name, host=host ) LOG.info("OSProfiler is enabled.\n" "Traces provided from the profiler " "can only be subscribed to using the same HMAC keys that " "are configured in Keystone's configuration file " "under the [profiler] section. \n To disable OSprofiler " "set in /etc/keystone/keystone.conf:\n" "[profiler]\n" "enabled=false")
# NOTE(morgan): ORDER HERE IS IMPORTANT! The middleware will process the # request in this list's order. _APP_MIDDLEWARE = ( _Middleware(namespace='keystone.server_middleware', ep='cors', conf={'oslo_config_project': 'keystone'}), _Middleware(namespace='keystone.server_middleware', ep='sizelimit', conf={}), _Middleware(namespace='keystone.server_middleware', ep='http_proxy_to_wsgi', conf={}), _Middleware(namespace='keystone.server_middleware', # OSProfiler 提供的 WSGI 中间件 ep='osprofiler', conf={}), _Middleware(namespace='keystone.server_middleware', ep='request_id', conf={}), )
defconfigure(conf): main_context_manager.configure(**dict(conf.database)) # NOTE(geguileo): To avoid a cyclical dependency we import the # group here. Dependency cycle is objects.base requires db.api, # which requires db.sqlalchemy.api, which requires service which # requires objects.base CONF.import_group("profiler", "cinder.service") if CONF.profiler.enabled: if CONF.profiler.trace_sqlalchemy: # 添加数据库追踪 lambda eng: osprofiler_sqlalchemy.add_tracing(sqlalchemy, eng, "db")
CONF = cfg.CONF CONF.register_opts(service_opts) if profiler_opts: profiler_opts.set_defaults(CONF)
defsetup_profiler(binary, host): if (osprofiler_initializer isNoneor profiler isNoneor profiler_opts isNone): LOG.debug('osprofiler is not present') return
if CONF.profiler.enabled: osprofiler_initializer.init_from_conf( conf=CONF, context=context.get_admin_context().to_dict(), project="cinder", service=binary, host=host ) LOG.warning( "OSProfiler is enabled.\nIt means that person who knows " "any of hmac_keys that are specified in " "/etc/cinder/cinder.conf can trace his requests. \n" "In real life only operator can read this file so there " "is no security issue. Note that even if person can " "trigger profiler, only admin user can retrieve trace " "information.\n" "To disable OSProfiler set in cinder.conf:\n" "[profiler]\nenabled=false")
classService(service.Service): """Service object for binaries running on hosts. A service takes a manager and enables rpc by listening to queues based on topic. It also periodically runs tasks on the manager and reports it state to the database services table. """ # Make service_id a class attribute so it can be used for clean up service_id = None
def_create_facade_lazily(): global _LOCK, _FACADE if _FACADE isNone: with _LOCK: if _FACADE isNone: _FACADE = session.EngineFacade.from_config(CONF) # 添加数据库追踪 if CONF.profiler.enabled and CONF.profiler.trace_sqlalchemy: osprofiler.sqlalchemy.add_tracing(sqlalchemy, _FACADE.get_engine(), "db") return _FACADE
@handle_redirects def_do_request(self, method, url, body, headers): """ Connects to the server and issues a request. Handles converting any returned HTTP error status codes to OpenStack/Glance exceptions and closing the server connection. Returns the result data, or raises an appropriate exception. :param method: HTTP method ("GET", "POST", "PUT", etc...) :param url: urlparse.ParsedResult object with URL information :param body: data to send (as string, filelike or iterable), or None (default) :param headers: mapping of key/value pairs to add as headers :note If the body param has a read attribute, and method is either POST or PUT, this method will automatically conduct a chunked-transfer encoding and use the body as a file object or iterable, transferring chunks of data using the connection's send() method. This allows large objects to be transferred efficiently without buffering the entire body in memory. """ if url.query: path = url.path + "?" + url.query else: path = url.path
defsetup_profiler(binary, host): if osprofiler and CONF.profiler.enabled: osprofiler.initializer.init_from_conf( conf=CONF, context=context.get_admin_context().to_dict(), project="nova", service=binary, host=host) LOG.info("OSProfiler is enabled.")
# 获取元数据 defget_traced_meta(): if profiler and'profiler'in CONF and CONF.profiler.enabled: return profiler.TracedMeta else: # NOTE(rpodolyaka): if we do not return a child of type, then Python # fails to build a correct MRO when osprofiler is not installed classNoopMeta(type): pass return NoopMeta
# 包装 osprofiler 类装饰器 deftrace_cls(name, **kwargs): """Wrap the OSProfiler trace_cls decorator so that it will not try to patch the class unless OSProfiler is present and enabled in the config :param name: The name of action. E.g. wsgi, rpc, db, etc.. :param kwargs: Any other keyword args used by profiler.trace_cls """
defdecorator(cls): if profiler and'profiler'in CONF and CONF.profiler.enabled: trace_decorator = profiler.trace_cls(name, kwargs) return trace_decorator(cls) return cls
# 元类 classManagerMeta(profiler.get_traced_meta(), type(PeriodicTasks)): """Metaclass to trace all children of a specific class. This metaclass wraps every public method (not starting with _ or __) of the class using it. All children classes of the class using ManagerMeta will be profiled as well. Adding this metaclass requires that the __trace_args__ attribute be added to the class we want to modify. That attribute is a dictionary with one mandatory key: "name". "name" defines the name of the action to be traced (for example, wsgi, rpc, db). The OSprofiler-based tracing, although, will only happen if profiler instance was initiated somewhere before in the thread, that can only happen if profiling is enabled in nova.conf and the API call to Nova API contained specific headers. """
# 返回追踪信息 def_serialize_profile_info(): ifnot profiler: returnNone prof = profiler.get() # 获取实例 trace_info = None if prof: # FIXME(DinaBelova): we'll add profiler.get_info() method # to extract this info -> we'll need to update these lines trace_info = { "hmac_key": prof.hmac_key, "base_id": prof.get_base_id(), "parent_id": prof.get_id() } return trace_info
# 装饰器 defspawn(func, *args, **kwargs): """Passthrough method for eventlet.spawn. This utility exists so that it can be stubbed for testing without interfering with the service spawns. It will also grab the context from the threadlocal store and add it to the store on the new thread. This allows for continuity in logging the context when using this method to spawn a new thread. """ _context = common_context.get_current() # 当前线程 profiler_info = _serialize_profile_info() # 追踪信息
@functools.wraps(func) defcontext_wrapper(*args, **kwargs): # NOTE: If update_store is not called after spawn it won't be # available for the logger to pull from threadlocal storage. if _context isnotNone: _context.update_store() if profiler_info and profiler: profiler.init(**profiler_info) # 初始化 osprofiler 实例 return func(*args, **kwargs)
# 装饰器 defspawn_n(func, *args, **kwargs): """Passthrough method for eventlet.spawn_n. This utility exists so that it can be stubbed for testing without interfering with the service spawns. It will also grab the context from the threadlocal store and add it to the store on the new thread. This allows for continuity in logging the context when using this method to spawn a new thread. """ _context = common_context.get_current() profiler_info = _serialize_profile_info()
@functools.wraps(func) defcontext_wrapper(*args, **kwargs): # NOTE: If update_store is not called after spawn_n it won't be # available for the logger to pull from threadlocal storage. if _context isnotNone: _context.update_store() if profiler_info and profiler: profiler.init(**profiler_info) func(*args, **kwargs)
defsetup(name, host='0.0.0.0'): # nosec """Setup OSprofiler notifier and enable profiling. :param name: name of the service, that will be profiled :param host: host (either host name or host address) the service will be running on. By default host will be set to 0.0.0.0, but more specified host name / address usage is highly recommended. """ if CONF.profiler.enabled: osprofiler.initializer.init_from_conf( # 启用 OSProfiler conf=CONF, context=context.get_admin_context().to_dict(), project="neutron", service=name, host=host ) LOG.info("OSProfiler is enabled.\n" "Traces provided from the profiler " "can only be subscribed to using the same HMAC keys that " "are configured in Neutron's configuration file " "under the [profiler] section.\n To disable OSprofiler " "set in /etc/neutron/neutron.conf:\n" "[profiler]\n" "enabled=false")
# 返回追踪信息 defcollect_profiler_info(): p = profiler.get() if p: return { "hmac_key": p.hmac_key, "base_id": p.get_base_id(), "parent_id": p.get_id(), }
# 装饰器 defspawn(func, *args, **kwargs): """As eventlet.spawn() but with osprofiler initialized in the new threads osprofiler stores the profiler instance in thread local storage, therefore in new threads (including eventlet threads) osprofiler comes uninitialized by default. This spawn() is a stand-in replacement for eventlet.spawn() but we re-initialize osprofiler in threads spawn()-ed. """
profiler_info = collect_profiler_info()
@functools.wraps(func) defwrapper(*args, **kwargs): if profiler_info: profiler.init(**profiler_info) return func(*args, **kwargs)
# 公有方法装饰器 classManagerMeta(profiler.get_traced_meta(), type(PeriodicTasks)): """Metaclass to trace all children of a specific class. This metaclass wraps every public method (not starting with _ or __) of the class using it. All children classes of the class using ManagerMeta will be profiled as well. Adding this metaclass requires that the __trace_args__ attribute be added to the class we want to modify. That attribute is a dictionary with one mandatory key: "name". "name" defines the name of the action to be traced (for example, wsgi, rpc, db). The OSprofiler-based tracing, although, will only happen if profiler instance was initiated somewhere before in the thread, that can only happen if profiling is enabled in nova.conf and the API call to Nova API contained specific headers. """