File indexing completed on 2026-04-25 08:29:11
0001 """
0002 Django settings for swf_monitor_project project.
0003
0004 Generated by 'django-admin startproject' using Django 4.2.23.
0005
0006 For more information on this file, see
0007 https://docs.djangoproject.com/en/4.2/topics/settings/
0008
0009 For the full list of settings and their values, see
0010 https://docs.djangoproject.com/en/4.2/ref/settings/
0011 """
0012
0013 from pathlib import Path
0014 import os
0015 from decouple import config
0016
0017
0018 BASE_DIR = Path(__file__).resolve().parent.parent
0019
0020
0021
0022 for _k in ("X509_USER_PROXY", "RUCIO_ACCOUNT", "RUCIO_HOST", "RUCIO_AUTH_HOST"):
0023 _v = config(_k, default=None)
0024 if _v:
0025 os.environ[_k] = _v
0026
0027
0028
0029
0030
0031
0032
0033 SECRET_KEY = config("SECRET_KEY")
0034
0035
0036
0037 DEBUG = config('DEBUG', default=True, cast=bool)
0038
0039
0040
0041
0042
0043
0044
0045
0046 ALLOWED_HOSTS = []
0047
0048
0049 allowed_hosts_str = config('SWF_ALLOWED_HOSTS', default='')
0050 if allowed_hosts_str:
0051 ALLOWED_HOSTS = [host.strip() for host in allowed_hosts_str.split(',')]
0052 elif DEBUG:
0053
0054 ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'testserver']
0055 else:
0056
0057
0058 pass
0059
0060
0061
0062
0063 INSTALLED_APPS = [
0064 "daphne",
0065 "channels",
0066
0067 "mcp_server",
0068 "oauth2_provider",
0069 "django.contrib.admin",
0070 "django.contrib.auth",
0071 "django.contrib.contenttypes",
0072 "django.contrib.sessions",
0073 "django.contrib.messages",
0074 "django.contrib.staticfiles",
0075 "pcs",
0076 "monitor_app",
0077 "django_dbml",
0078
0079 "rest_framework",
0080 "drf_spectacular",
0081 "rest_framework.authtoken",
0082 "django_seed",
0083 "django_extensions",
0084 ]
0085
0086 MIDDLEWARE = [
0087 "django.middleware.security.SecurityMiddleware",
0088 "whitenoise.middleware.WhiteNoiseMiddleware",
0089 "django.contrib.sessions.middleware.SessionMiddleware",
0090 "django.middleware.common.CommonMiddleware",
0091 "django.middleware.csrf.CsrfViewMiddleware",
0092 "monitor_app.middleware.MCPAuthMiddleware",
0093 "django.contrib.auth.middleware.AuthenticationMiddleware",
0094 "monitor_app.middleware.TunnelAuthMiddleware",
0095 "django.contrib.messages.middleware.MessageMiddleware",
0096 "django.middleware.clickjacking.XFrameOptionsMiddleware",
0097 ]
0098
0099 ROOT_URLCONF = "swf_monitor_project.urls"
0100
0101 TEMPLATES = [
0102 {
0103 "BACKEND": "django.template.backends.django.DjangoTemplates",
0104 "DIRS": [os.path.join(BASE_DIR, 'templates')],
0105 "APP_DIRS": True,
0106 "OPTIONS": {
0107 "context_processors": [
0108 "django.template.context_processors.debug",
0109 "django.template.context_processors.request",
0110 "django.contrib.auth.context_processors.auth",
0111 "django.contrib.messages.context_processors.messages",
0112 "monitor_app.middleware.tunnel_context",
0113 ],
0114 },
0115 },
0116 ]
0117
0118 WSGI_APPLICATION = "swf_monitor_project.wsgi.application"
0119 ASGI_APPLICATION = "swf_monitor_project.asgi.application"
0120
0121
0122
0123
0124
0125 DATABASES = {
0126 "default": {
0127 "ENGINE": "django.db.backends.postgresql",
0128 "NAME": config("DB_NAME", default="swfdb"),
0129 "USER": config("DB_USER", default="admin"),
0130 "PASSWORD": config("DB_PASSWORD", default="dummy"),
0131 "HOST": config("DB_HOST", default="localhost"),
0132 "PORT": config("DB_PORT", default="5432"),
0133 },
0134 "panda": {
0135 "ENGINE": "django.db.backends.postgresql",
0136 "NAME": config("PANDA_DB_NAME", default="panda_db"),
0137 "USER": config("PANDA_DB_USER", default="panda"),
0138 "PASSWORD": config("PANDA_DB_PASSWORD", default="dummy"),
0139 "HOST": config("PANDA_DB_HOST", default="pandadb01.sdcc.bnl.gov"),
0140 "PORT": config("PANDA_DB_PORT", default="5432"),
0141 },
0142 "idds": {
0143 "ENGINE": "django.db.backends.postgresql",
0144 "NAME": config("IDDS_DB_NAME", default="idds_db"),
0145 "USER": config("IDDS_DB_USER", default="idds"),
0146 "PASSWORD": config("IDDS_DB_PASSWORD", default="dummy"),
0147 "HOST": config("IDDS_DB_HOST", default="pandadb01.sdcc.bnl.gov"),
0148 "PORT": config("IDDS_DB_PORT", default="5432"),
0149 "OPTIONS": {
0150 "options": f"-c search_path={config('IDDS_DB_SCHEMA', default='doma_idds')}"
0151 },
0152 }
0153 }
0154
0155
0156
0157
0158
0159 AUTH_PASSWORD_VALIDATORS = [
0160 {
0161 "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
0162 },
0163 {
0164 "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
0165 },
0166 {
0167 "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
0168 },
0169 {
0170 "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
0171 },
0172 ]
0173
0174
0175
0176
0177
0178 LANGUAGE_CODE = "en-us"
0179
0180 TIME_ZONE = config('DISPLAY_TIMEZONE', default='America/New_York')
0181
0182 USE_I18N = True
0183
0184 USE_TZ = True
0185
0186
0187
0188
0189 DEPLOYMENT_SUBPATH = config('SWF_DEPLOYMENT_SUBPATH', default='')
0190
0191 if DEPLOYMENT_SUBPATH:
0192
0193 FORCE_SCRIPT_NAME = DEPLOYMENT_SUBPATH
0194 STATIC_URL = config('SWF_STATIC_URL_BASE', default=f'{DEPLOYMENT_SUBPATH}/static/')
0195 LOGIN_REDIRECT_URL = config('SWF_LOGIN_REDIRECT', default=f'{DEPLOYMENT_SUBPATH}/prod/')
0196 LOGOUT_REDIRECT_URL = config('SWF_LOGOUT_REDIRECT', default=DEPLOYMENT_SUBPATH + '/prod/')
0197 else:
0198
0199 STATIC_URL = "static/"
0200 LOGIN_REDIRECT_URL = '/prod/'
0201 LOGOUT_REDIRECT_URL = '/prod/'
0202
0203
0204
0205
0206 STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
0207 STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
0208
0209
0210 STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
0211
0212 LOGIN_URL = 'login'
0213
0214
0215
0216
0217
0218 DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
0219
0220
0221
0222 REST_FRAMEWORK = {
0223
0224
0225 "DEFAULT_PERMISSION_CLASSES": [
0226 "rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly"
0227 ],
0228 "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
0229 "DEFAULT_RENDERER_CLASSES": [
0230 "rest_framework.renderers.JSONRenderer",
0231 "rest_framework.renderers.BrowsableAPIRenderer",
0232 ],
0233 }
0234
0235 SPECTACULAR_SETTINGS = {
0236 "TITLE": "SWF Monitor API",
0237 "DESCRIPTION": "API for the ePIC Streaming Workflow Testbed Monitor",
0238 "VERSION": "1.0.0",
0239 "SERVE_INCLUDE_SCHEMA": False,
0240
0241 }
0242
0243
0244 OAUTH2_PROVIDER = {
0245 "SCOPES": {
0246 "read": "Read access to MCP tools",
0247 "write": "Write access to MCP tools",
0248 },
0249 "ACCESS_TOKEN_EXPIRE_SECONDS": 3600,
0250 }
0251
0252
0253
0254 AUTH0_DOMAIN = config("AUTH0_DOMAIN", default="")
0255 AUTH0_CLIENT_ID = config("AUTH0_CLIENT_ID", default="")
0256 AUTH0_CLIENT_SECRET = config("AUTH0_CLIENT_SECRET", default="")
0257 AUTH0_API_IDENTIFIER = config("AUTH0_API_IDENTIFIER", default="")
0258 AUTH0_ALGORITHMS = ["RS256"]
0259
0260
0261 DJANGO_MCP_GLOBAL_SERVER_CONFIG = {
0262 "name": "swf-testbed",
0263 "instructions": """Streaming workflow orchestration testbed for the ePIC experiment at the Electron Ion Collider.
0264
0265 KEY CONCEPTS:
0266 - Namespaces: Isolation boundaries for different users' workflow runs (e.g., 'torre1', 'wenauseic')
0267 - Runs: Data-taking periods identified by run_number, containing multiple STF files
0268 - STF files: Super Time Frame files - primary data units from the detector DAQ system
0269 - TF slices: Small processing units (~15 per STF) for fast monitoring workflow
0270 - Agents: Processes that execute workflows (daq_simulator, data_agent, processing_agent, etc.)
0271 - Workflow executions: Instances of running workflows, tracked by execution_id
0272
0273 COMMON QUERIES:
0274 - What's running now? → swf_list_workflow_executions(currently_running=True)
0275 - Any errors? → swf_list_logs(level='ERROR')
0276 - System health? → swf_get_system_state()
0277 - Activity in a namespace? → swf_get_namespace(namespace='name')
0278 - Failed workflows? → swf_list_workflow_executions(status='failed')
0279 - Send announcement/test message? → swf_send_message(message='...', message_type='announcement')
0280 - What's PanDA doing? → panda_get_activity() — compact overview, no individual records
0281 - PanDA task overview? → panda_list_tasks(days=7)
0282 - Failed PanDA tasks? → panda_list_tasks(status='failed')
0283 - EIC experiment tasks? → panda_list_tasks(workinggroup='EIC')
0284 - Production tasks? → panda_list_tasks(processingtype='epicproduction')
0285 - Top errors? → panda_error_summary(days=7)
0286 - Errors for a user? → panda_error_summary(username='someone')
0287 - Deep dive on a failed job? → panda_study_job(pandaid=130497)
0288 - EIC queues? → panda_list_queues(vo='eic')
0289 - Queue config? → panda_get_queue(panda_queue='NERSC_Perlmutter_epic')
0290 - Core-hours this month? → panda_resource_usage(days=30)
0291 - Core-hours on Perlmutter? → panda_resource_usage(days=30, site='NERSC_Perlmutter%')
0292 - What is PCS? → PCS = Physics Configuration System, manages configuration of production tasks based on physics inputs
0293 - List physics tags? → pcs_list_tags(tag_type='p')
0294 - What is tag p1001? → pcs_get_tag(tag_label='p1001')
0295 - Reco tags? → pcs_list_tags(tag_type='r')
0296 - Photoproduction tags? → pcs_search_tags(query='photoproduction')
0297 - DIS tags? → pcs_list_tags(tag_type='p', category='DIS')
0298 - Tags using pythia8? → pcs_search_tags(query='pythia8')
0299
0300 PCS (Physics Configuration System):
0301 PCS manages the configuration of production tasks based on physics inputs for ePIC Monte Carlo simulation campaigns. Configurations are
0302 organized as tags — named parameter sets for each pipeline stage:
0303 - Physics tags (p): process, beam energies, species, Q2 range (e.g. p1001 = DIS NC 10x100 ep)
0304 - EvGen tags (e): event generator and version (e.g. e1 = pythia8 8.310)
0305 - Simu tags (s): detector simulation config (e.g. s1 = npsim 26.02.0)
0306 - Reco tags (r): reconstruction config (e.g. r1 = eicrecon 26.02.0)
0307 Physics tags are grouped by category: DIS (p1xxx), DVCS (p2xxx), SIDIS (p3xxx), EXCLUSIVE (p4xxx).
0308 Tags are draft (editable) or locked (immutable, for production use).
0309
0310 AFTER swf_start_workflow — ACTIVELY POLL, DO NOT SLEEP:
0311 Poll swf_get_workflow_monitor(execution_id) every 10-15s until completion.
0312 Report progress to user as it evolves. Check swf_list_logs(level='ERROR') after.
0313
0314 FILTERING:
0315 - All list tools support start_time/end_time parameters (ISO datetime strings)
0316 - Status filters are case-insensitive
0317 - Context cascades: run_number → stf_filename → tf_filename
0318
0319 Use swf_list_available_tools() to see all available tools with descriptions.""",
0320 }
0321
0322
0323 DJANGO_MCP_ENDPOINT = ""
0324
0325
0326
0327
0328
0329
0330
0331 ACTIVEMQ_HOST = config('ACTIVEMQ_HOST', default='localhost')
0332 ACTIVEMQ_PORT = config('ACTIVEMQ_PORT', default=61612, cast=int)
0333 ACTIVEMQ_USER = config('ACTIVEMQ_USER', default='admin')
0334 ACTIVEMQ_PASSWORD = config('ACTIVEMQ_PASSWORD', default='admin')
0335 ACTIVEMQ_HEARTBEAT_TOPIC = config('ACTIVEMQ_HEARTBEAT_TOPIC', default='/topic/heartbeat')
0336 ACTIVEMQ_USE_SSL = config('ACTIVEMQ_USE_SSL', default=False, cast=bool)
0337 ACTIVEMQ_SSL_CERT_FILE = config('ACTIVEMQ_SSL_CERT_FILE', default='')
0338 ACTIVEMQ_SSL_KEY_FILE = config('ACTIVEMQ_SSL_KEY_FILE', default='')
0339 ACTIVEMQ_SSL_CA_CERTS = config('ACTIVEMQ_SSL_CA_CERTS', default='')
0340
0341
0342
0343 REDIS_URL = config('REDIS_URL', default='')
0344 if REDIS_URL:
0345 CHANNEL_LAYERS = {
0346 "default": {
0347 "BACKEND": "channels_redis.core.RedisChannelLayer",
0348 "CONFIG": {
0349 "hosts": [REDIS_URL],
0350 },
0351 }
0352 }
0353 else:
0354 CHANNEL_LAYERS = {
0355 "default": {
0356 "BACKEND": "channels.layers.InMemoryChannelLayer"
0357 }
0358 }
0359
0360
0361 SSE_CHANNEL_GROUP = config('SSE_CHANNEL_GROUP', default='workflow_events')
0362
0363
0364
0365 LOGGING_MODE = os.getenv('DJANGO_LOGGING_MODE', 'normal')
0366
0367 if LOGGING_MODE == 'none':
0368
0369 LOGGING = {
0370 'version': 1,
0371 'disable_existing_loggers': False,
0372 'handlers': {
0373 'null': {
0374 'class': 'logging.NullHandler',
0375 },
0376 },
0377 'root': {
0378 'handlers': ['null'],
0379 },
0380 }
0381 else:
0382
0383 LOGGING = {
0384 'version': 1,
0385 'disable_existing_loggers': False,
0386 'formatters': {
0387 'json': {
0388 '()': 'pythonjsonlogger.json.JsonFormatter',
0389 'format': '%(asctime)s %(name)s %(levelname)s %(module)s %(funcName)s %(lineno)d %(message)s',
0390 'rename_fields': {
0391 'funcName': 'funcname'
0392 }
0393 },
0394 },
0395 'handlers': {
0396 'console': {
0397 'class': 'logging.StreamHandler',
0398 'formatter': 'json',
0399 },
0400 'rest': {
0401 'class': 'swf_common_lib.logging_utils.RestLogHandler',
0402 'url': 'http://localhost:8002/api/logs/',
0403 'formatter': 'json',
0404 },
0405 'db': {
0406 'class': 'monitor_app.db_log_handler.DbLogHandler',
0407 'level': 'INFO',
0408 },
0409 },
0410 'root': {
0411 'handlers': ['console'],
0412 'level': 'INFO',
0413 },
0414 'loggers': {
0415 'django': {
0416 'handlers': ['console'],
0417 'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
0418 'propagate': False,
0419 },
0420 'monitor_app': {
0421 'handlers': ['db', 'console'],
0422 'level': 'INFO',
0423 'propagate': False,
0424 },
0425 },
0426 }