dbca-utils 3.0.7__tar.gz → 3.0.8__tar.gz
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.
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/PKG-INFO +9 -4
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/README.md +8 -3
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/pyproject.toml +1 -1
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/src/dbca_utils/healthcheck/healthcheck.py +274 -242
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/LICENSE +0 -0
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/src/dbca_utils/__init__.py +0 -0
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/src/dbca_utils/apps.py +0 -0
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/src/dbca_utils/healthcheck/__init__.py +0 -0
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/src/dbca_utils/healthcheck/urls.py +0 -0
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/src/dbca_utils/middleware.py +0 -0
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/src/dbca_utils/models.py +0 -0
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/src/dbca_utils/utils.py +0 -0
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/tests/__init__.py +0 -0
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/tests/apps.py +0 -0
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/tests/migrations/0001_initial.py +0 -0
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/tests/migrations/__init__.py +0 -0
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/tests/models.py +0 -0
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/tests/settings.py +0 -0
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/tests/templates/tests/test_model_list.html +0 -0
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/tests/tests.py +0 -0
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/tests/urls.py +0 -0
- {dbca_utils-3.0.7 → dbca_utils-3.0.8}/tests/views.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dbca-utils
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.8
|
|
4
4
|
Summary: Utilities for DBCA Django apps
|
|
5
5
|
Author-Email: Rocky Chen <rocky.chen@dbca.wa.gov.au>, Ashley Felton <ashley.felton@dbca.wa.gov.au>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -106,18 +106,23 @@ MIDDLEWARE = [
|
|
|
106
106
|
### Requirements
|
|
107
107
|
- Django 5.2 or later
|
|
108
108
|
- Declared the default cache and also the cache is shared by all pod instances
|
|
109
|
-
- The image has the command 'ps' which is used to collect the cpu and
|
|
109
|
+
- The image has the command 'ps' which is used to collect the cpu,memory and persistent volume usage data.
|
|
110
110
|
|
|
111
111
|
### Usage
|
|
112
112
|
- Install the app 'dbca_utils' in INSTALLED_APPS
|
|
113
113
|
- Service Configuration
|
|
114
114
|
- HEALTHCHECK_ENABLED: Optional. enable/disable the healthcheck service. default is 'true'
|
|
115
|
-
-
|
|
116
|
-
- CACHE_PREFIX: Optional. used as the prefix of the cache key. default is ''
|
|
115
|
+
- CACHE_PREFIX: Optional. used as the prefix WORKLOAD_VOLUMESof WORKLOAD_VOLUMESthe cache key. default is ''
|
|
117
116
|
- PORT: Optional. The listening port of the web application. default is '8080'
|
|
118
117
|
- WORKLOADS: Optional. Used if the web app has a fixed replicas.
|
|
119
118
|
- WORKLOAD_DEPLOYMENT: Optional. the workload is deployment if it is true; otherwise it is statefulset. default is 'true'
|
|
120
119
|
- WORKLOAD_FAILED_THRESHOLD: Optional. The number of continuous failed times to treat a pod is offline.
|
|
120
|
+
- WORKLOAD_VOLUMES: Optional. can be 'disabled', 'false','automatic' or "," separated volume mounted point. default is "automatic".
|
|
121
|
+
- disabled|false: Don't harvest volume usage data
|
|
122
|
+
- automatic: Detect the persistent volume automatically.
|
|
123
|
+
- "," separated volume mounted points: the volume list
|
|
124
|
+
- HEALTHCHECK_SYSTEMDATA_ENABLED: Enable system resource data
|
|
125
|
+
- HEALTHCHECK_PROCESSDATA_ENABLED: Enable process resource data
|
|
121
126
|
- Nginx Configuration.
|
|
122
127
|
- Add a location 'location /healthcheck/' and configure it to use basic auth in nginx.
|
|
123
128
|
- Access the url : https://xxx.dbca.wa.gov.au/healthcheck/healthdata to get the health json data
|
|
@@ -78,18 +78,23 @@ MIDDLEWARE = [
|
|
|
78
78
|
### Requirements
|
|
79
79
|
- Django 5.2 or later
|
|
80
80
|
- Declared the default cache and also the cache is shared by all pod instances
|
|
81
|
-
- The image has the command 'ps' which is used to collect the cpu and
|
|
81
|
+
- The image has the command 'ps' which is used to collect the cpu,memory and persistent volume usage data.
|
|
82
82
|
|
|
83
83
|
### Usage
|
|
84
84
|
- Install the app 'dbca_utils' in INSTALLED_APPS
|
|
85
85
|
- Service Configuration
|
|
86
86
|
- HEALTHCHECK_ENABLED: Optional. enable/disable the healthcheck service. default is 'true'
|
|
87
|
-
-
|
|
88
|
-
- CACHE_PREFIX: Optional. used as the prefix of the cache key. default is ''
|
|
87
|
+
- CACHE_PREFIX: Optional. used as the prefix WORKLOAD_VOLUMESof WORKLOAD_VOLUMESthe cache key. default is ''
|
|
89
88
|
- PORT: Optional. The listening port of the web application. default is '8080'
|
|
90
89
|
- WORKLOADS: Optional. Used if the web app has a fixed replicas.
|
|
91
90
|
- WORKLOAD_DEPLOYMENT: Optional. the workload is deployment if it is true; otherwise it is statefulset. default is 'true'
|
|
92
91
|
- WORKLOAD_FAILED_THRESHOLD: Optional. The number of continuous failed times to treat a pod is offline.
|
|
92
|
+
- WORKLOAD_VOLUMES: Optional. can be 'disabled', 'false','automatic' or "," separated volume mounted point. default is "automatic".
|
|
93
|
+
- disabled|false: Don't harvest volume usage data
|
|
94
|
+
- automatic: Detect the persistent volume automatically.
|
|
95
|
+
- "," separated volume mounted points: the volume list
|
|
96
|
+
- HEALTHCHECK_SYSTEMDATA_ENABLED: Enable system resource data
|
|
97
|
+
- HEALTHCHECK_PROCESSDATA_ENABLED: Enable process resource data
|
|
93
98
|
- Nginx Configuration.
|
|
94
99
|
- Add a location 'location /healthcheck/' and configure it to use basic auth in nginx.
|
|
95
100
|
- Access the url : https://xxx.dbca.wa.gov.au/healthcheck/healthdata to get the health json data
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import importlib
|
|
3
3
|
import logging
|
|
4
|
+
import psutil
|
|
4
5
|
import subprocess
|
|
5
6
|
import random
|
|
6
7
|
import re
|
|
@@ -10,6 +11,7 @@ import requests
|
|
|
10
11
|
from datetime import datetime
|
|
11
12
|
|
|
12
13
|
from django.urls import reverse,path,include
|
|
14
|
+
from django.utils import timezone
|
|
13
15
|
from django.conf import settings
|
|
14
16
|
from django.http import HttpResponseForbidden, JsonResponse,HttpResponseServerError
|
|
15
17
|
from django.core.signals import request_started
|
|
@@ -24,7 +26,9 @@ HEALTHCHECK_ENABLED = os.environ.get("HEALTHCHECK_ENABLED","true").lower() == "t
|
|
|
24
26
|
if not HEALTHCHECK_ENABLED:
|
|
25
27
|
HEALTHCHECK_ENABLED = True if cache else None
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
HEALTHCHECK_SYSTEMDATA_ENABLED = os.environ.get("HEALTHCHECK_SYSTEMDATA_ENABLED","true").lower() == "true"
|
|
30
|
+
HEALTHCHECK_PROCESSDATA_ENABLED = os.environ.get("HEALTHCHECK_PROCESSDATA_ENABLED","true").lower() == "true"
|
|
31
|
+
|
|
28
32
|
CACHE_PREFIX = os.environ.get("CACHE_PREFIX","")
|
|
29
33
|
PORT = int(os.environ.get("WORKLOAD_PORT",8080))
|
|
30
34
|
WORKLOADS = int(os.environ.get("WORKLOADS",0))
|
|
@@ -80,138 +84,11 @@ else:
|
|
|
80
84
|
|
|
81
85
|
ip = get_local_ip()
|
|
82
86
|
|
|
83
|
-
webapp_process_registerfolder = "/tmp/__webapp__/proc"
|
|
84
|
-
|
|
85
|
-
def get_processregisterfile(pid):
|
|
86
|
-
return os.path.join(webapp_process_registerfolder,str(pid))
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def register_webappprocess():
|
|
90
|
-
"""
|
|
91
|
-
Register all webapp related processes
|
|
92
|
-
Healthcheck will use the processes to calculate the resources used by webapp
|
|
93
|
-
"""
|
|
94
|
-
pid = os.getpid()
|
|
95
|
-
logger.debug("Register the webapp process '{}({}).{}'.".format(hostname,ip,pid))
|
|
96
|
-
try:
|
|
97
|
-
if not os.path.exists(webapp_process_registerfolder):
|
|
98
|
-
os.makedirs(webapp_process_registerfolder)
|
|
99
|
-
|
|
100
|
-
registerfile = get_processregisterfile(pid)
|
|
101
|
-
#register the webapp process first
|
|
102
|
-
with open(registerfile,"wt") as f:
|
|
103
|
-
f.write(datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f"))
|
|
104
|
-
except Exception as ex:
|
|
105
|
-
logger.error("Failed to register the webapp process '{}({}).{}'.".format(hostname,ip,pid))
|
|
106
|
-
|
|
107
|
-
def unregister_webappprocess():
|
|
108
|
-
pid = os.getpid()
|
|
109
|
-
logger.debug("Unregister the webapp process '{}({}).{}'.".format(hostname,ip,pid))
|
|
110
|
-
try:
|
|
111
|
-
registerfile = get_processregisterfile(pid)
|
|
112
|
-
#register the webapp process first
|
|
113
|
-
os.remove(registerfile)
|
|
114
|
-
except Exception as ex:
|
|
115
|
-
if os.path.exists(registerfile):
|
|
116
|
-
logger.error("Failed to unregister the webapp process '{}({}).{}'.".format(hostname,ip,pid))
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
GET_VOLUMEUSAGE_CMD = None
|
|
120
|
-
def get_persistent_volumes_data():
|
|
121
|
-
global WORKLOAD_VOLUMES
|
|
122
|
-
global GET_VOLUMEUSAGE_CMD
|
|
123
|
-
try:
|
|
124
|
-
if WORKLOAD_VOLUMES == []:
|
|
125
|
-
return {}
|
|
126
|
-
elif GET_VOLUMEUSAGE_CMD:
|
|
127
|
-
cmd = GET_VOLUMEUSAGE_CMD
|
|
128
|
-
else:
|
|
129
|
-
if WORKLOAD_VOLUMES is None:
|
|
130
|
-
#not configured. should find them automatically,
|
|
131
|
-
#the detect logic is dedicated for kubenetes
|
|
132
|
-
cmd = 'df --output="source,target,size"'
|
|
133
|
-
result = subprocess.run(cmd,shell=True,capture_output=True,text=True)
|
|
134
|
-
volumes = []
|
|
135
|
-
datarow = False
|
|
136
|
-
volumesdata = {}
|
|
137
|
-
overlaysize = 0
|
|
138
|
-
for line in result.stdout.split("\n"):
|
|
139
|
-
line = line.strip()
|
|
140
|
-
if not line:
|
|
141
|
-
continue
|
|
142
|
-
if not datarow:
|
|
143
|
-
line = line.lower()
|
|
144
|
-
if line.startswith("filesystem") :
|
|
145
|
-
datarow = True
|
|
146
|
-
continue
|
|
147
|
-
source,target,size = line.split()
|
|
148
|
-
size = int(size)
|
|
149
|
-
source = source.lower()
|
|
150
|
-
if source == "overlay":
|
|
151
|
-
overlaysize = size
|
|
152
|
-
elif source.startswith("//"):
|
|
153
|
-
volumes.append(target)
|
|
154
|
-
elif source.startswith("/dev/sd"):
|
|
155
|
-
#the volume can be same filesystem as the volume 'overlay'
|
|
156
|
-
volumesdata[target] = size
|
|
157
|
-
|
|
158
|
-
for k,v in volumesdata.items():
|
|
159
|
-
if v == overlaysize:
|
|
160
|
-
#the volume is the same volume as overlay
|
|
161
|
-
continue
|
|
162
|
-
volumes.append(k)
|
|
163
|
-
|
|
164
|
-
WORKLOAD_VOLUMES = volumes
|
|
165
|
-
if volumes:
|
|
166
|
-
cmd = 'df --output="target,size,used" -BK {}'.format(" ".join(volumes))
|
|
167
|
-
GET_VOLUMEUSAGE_CMD = cmd
|
|
168
|
-
else:
|
|
169
|
-
return {}
|
|
170
|
-
else:
|
|
171
|
-
volumes = WORKLOAD_VOLUMES
|
|
172
|
-
cmd = 'df --output="target,size,used" -BK {}'.format(" ".join(volumes))
|
|
173
|
-
|
|
174
|
-
result = subprocess.run(cmd,shell=True,capture_output=True,text=True)
|
|
175
|
-
volumesdata = {}
|
|
176
|
-
datarow = False
|
|
177
|
-
for line in result.stdout.split("\n"):
|
|
178
|
-
line = line.strip()
|
|
179
|
-
if not line:
|
|
180
|
-
continue
|
|
181
|
-
if not datarow:
|
|
182
|
-
line = line.lower()
|
|
183
|
-
if line.startswith("mounted on") :
|
|
184
|
-
datarow = True
|
|
185
|
-
continue
|
|
186
|
-
target,size,used = line.split()
|
|
187
|
-
size = int(size[:-1])
|
|
188
|
-
used = int(used[:-1])
|
|
189
|
-
if size / 1048576 >= 10:
|
|
190
|
-
#large than 10G, use 'G' as unit
|
|
191
|
-
volumesdata[target] = {"size":size/1048576,"used":used / 1048576,"pcent":100 * used/size,"unit":"G"}
|
|
192
|
-
elif size / 1024 >= 10:
|
|
193
|
-
#large than 10M, use 'M' as unit
|
|
194
|
-
volumesdata[target] = {"size":size/1024,"used":used / 1024,"pcent":100 * used/size,"unit":"M"}
|
|
195
|
-
else:
|
|
196
|
-
volumesdata[target] = {"size":size,"used":used,"pcent":100 * used/size}
|
|
197
|
-
|
|
198
|
-
if not GET_VOLUMEUSAGE_CMD:
|
|
199
|
-
#This is the first time to get the volume usage, delete the non-exist volume from volumes
|
|
200
|
-
for i in range(len(volumes) - 1,-1,-1):
|
|
201
|
-
if volumes[i] not in volumesdata:
|
|
202
|
-
del volumes[i]
|
|
203
|
-
WORKLOAD_VOLUMES = volumes
|
|
204
|
-
GET_VOLUMEUSAGE_CMD = 'df --output="target,size,used" -BK {}'.format(" ".join(volumes))
|
|
205
|
-
return volumesdata
|
|
206
|
-
except Exception as ex:
|
|
207
|
-
return "Failed to volumes usage data.{}: {}".format(ex.__class__.__name__,str(ex))
|
|
208
|
-
|
|
209
87
|
item_version = "__version__"
|
|
210
88
|
key_workloads = "{}__workloads__".format(CACHE_PREFIX)
|
|
211
89
|
key_workloads_lock = "{}lock__".format(key_workloads)
|
|
212
90
|
|
|
213
|
-
|
|
214
|
-
def register_webappserver(sender,environ,**kwargs):
|
|
91
|
+
def register_webappserver(sender,*args,**kwargs):
|
|
215
92
|
"""
|
|
216
93
|
Register a web server running in the same workload
|
|
217
94
|
1. Write a server register file in workload's local file system
|
|
@@ -279,66 +156,180 @@ if HEALTHCHECK_ENABLED:
|
|
|
279
156
|
#healthcheck is not initied
|
|
280
157
|
request_started.connect(register_webappserver,dispatch_uid="register_webappserver")
|
|
281
158
|
|
|
282
|
-
GET_RESOURCEUSAGE_CMD = "ps ax -o %cpu=,vsz=,rss=,cmd= {}".format(PROCESS_FILTER).strip()
|
|
283
|
-
GET_RESOURCEUSAGE_PIPECMDS = [c.strip() for c in GET_RESOURCEUSAGE_CMD.split("|")]
|
|
284
159
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
160
|
+
VALID_WORKLOAD_VOLUMES = None
|
|
161
|
+
def get_volumes_healthdata():
|
|
162
|
+
global VALID_WORKLOAD_VOLUMES
|
|
163
|
+
try:
|
|
164
|
+
if VALID_WORKLOAD_VOLUMES is None:
|
|
165
|
+
volumes = []
|
|
166
|
+
for partition in psutil.disk_partitions(all=True):
|
|
167
|
+
if WORKLOAD_VOLUMES is None:
|
|
168
|
+
if partition.fstype.lower() not in ("cifs","nfs","sshfs","davfs2"):
|
|
169
|
+
continue
|
|
170
|
+
volumes.append(partition.mountpoint)
|
|
171
|
+
elif partition.mountpoint in WORKLOAD_VOLUMES:
|
|
172
|
+
volumes.append(partition.mountpoint)
|
|
173
|
+
|
|
174
|
+
VALID_WORKLOAD_VOLUMES = volumes
|
|
175
|
+
|
|
176
|
+
if not VALID_WORKLOAD_VOLUMES:
|
|
177
|
+
return {}
|
|
178
|
+
|
|
179
|
+
volumesdata = {}
|
|
180
|
+
for volume in VALID_WORKLOAD_VOLUMES:
|
|
181
|
+
diskusage = psutil.disk_usage(volume)
|
|
182
|
+
if diskusage.total / 1073741824 >= 10:
|
|
183
|
+
#large than 10G, use 'G' as unit
|
|
184
|
+
volumesdata[volume] = {"size":round(diskusage.total / 1073741824),"used":round(diskusage.used / 1073741824),"pcent":100 * diskusage.used/diskusage.total,"unit":"G"}
|
|
185
|
+
elif diskusage.total / 1048576 >= 10:
|
|
186
|
+
#large than 10M, use 'M' as unit
|
|
187
|
+
volumesdata[volume] = {"size":round(diskusage.total / 1048576),"used":round(diskusage.used / 1048576),"pcent":100 * diskusage.used/diskusage.total,"unit":"M"}
|
|
188
|
+
else:
|
|
189
|
+
volumesdata[volume] = {"size":round(diskusage.total / 1024),"used":round(diskusage.used / 1024),"pcent":100 * diskusage.used/diskusage.total,"unit":"K"}
|
|
190
|
+
|
|
191
|
+
return volumesdata
|
|
192
|
+
except Exception as ex:
|
|
193
|
+
import traceback;
|
|
194
|
+
traceback.print_exc()
|
|
195
|
+
return "Failed to volumes usage data.{}: {}".format(ex.__class__.__name__,str(ex))
|
|
196
|
+
|
|
197
|
+
def get_workload_system_healthdata():
|
|
198
|
+
cpu_pcent = system_cpu_pcents = psutil.cpu_percent(percpu=False)
|
|
199
|
+
cpucores_pcent = system_cpu_pcents = psutil.cpu_percent(percpu=True)
|
|
200
|
+
memoryinfo = psutil.virtual_memory()
|
|
201
|
+
netio = psutil.net_io_counters()
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
"cpu_pcent":cpu_pcent,
|
|
205
|
+
"cpucores_pcent":cpucores_pcent,
|
|
206
|
+
"memory_total": memoryinfo.total / 1073741824,
|
|
207
|
+
"memory_used": (memoryinfo.total - memoryinfo.available) / 1073741824,
|
|
208
|
+
"memory_pcent": (memoryinfo.total - memoryinfo.available) * 100 / memoryinfo.total,
|
|
209
|
+
"bytes_sent": netio.bytes_sent,
|
|
210
|
+
"bytes_recv": netio.bytes_recv
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
def get_process_healthdata(proc):
|
|
214
|
+
memoryinfo = proc.memory_info()
|
|
215
|
+
result = {
|
|
216
|
+
"start_time":timezone.make_aware(datetime.fromtimestamp(proc.create_time())).strftime("%Y-%m-%dT%H:%M:%S"),
|
|
217
|
+
"cmdline":proc.cmdline(),
|
|
218
|
+
"cpu_num": proc.cpu_num(),
|
|
219
|
+
"cpu_pcent": proc.cpu_percent(),
|
|
220
|
+
"pmemory":memoryinfo.rss / 1048576,
|
|
221
|
+
"vmemory":memoryinfo.vms / 1048576
|
|
222
|
+
}
|
|
223
|
+
if proc.pid == curprocpid:
|
|
224
|
+
result["currentprocess"] = True
|
|
225
|
+
return result
|
|
226
|
+
|
|
227
|
+
rootproc = None
|
|
228
|
+
curprocpid = None
|
|
229
|
+
def get_workload_app_healthdata(perprocess=True):
|
|
230
|
+
"""
|
|
231
|
+
All processes belonging to the webapp should have
|
|
232
|
+
1. same parent process
|
|
233
|
+
2. the cmdline of all processes should be same
|
|
234
|
+
"""
|
|
235
|
+
global rootproc
|
|
236
|
+
global curprocpid
|
|
237
|
+
if not curprocpid:
|
|
238
|
+
curprocpid = os.getpid()
|
|
239
|
+
|
|
240
|
+
if not rootproc:
|
|
241
|
+
#the the root proc
|
|
242
|
+
#get the pid of the current process
|
|
243
|
+
curproc = psutil.Process(curprocpid)
|
|
244
|
+
#find the parent
|
|
245
|
+
pproc = curproc
|
|
246
|
+
rootproc = None
|
|
247
|
+
app_cmdline = curproc.cmdline()
|
|
248
|
+
while not rootproc:
|
|
249
|
+
ppid = pproc.ppid()
|
|
250
|
+
if not ppid:
|
|
251
|
+
rootproc = pproc
|
|
252
|
+
continue
|
|
253
|
+
|
|
254
|
+
tmpproc = psutil.Process(ppid)
|
|
255
|
+
tmpproc_cmdline = tmpproc.cmdline()
|
|
256
|
+
if tmpproc_cmdline == app_cmdline:
|
|
257
|
+
#the pproc has the same cmd line as current proc. the pproc is also related app python process
|
|
258
|
+
pproc = tmpproc
|
|
259
|
+
elif any(any(key in p for key in ("python","gunicorn","uwsgi","django")) for p in tmpproc_cmdline):
|
|
260
|
+
#the pproc is still the python process.
|
|
261
|
+
pproc = tmpproc
|
|
262
|
+
else:
|
|
263
|
+
rootproc = pproc
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
#find all realted processes and its health data
|
|
267
|
+
rootproc_data = get_process_healthdata(rootproc)
|
|
268
|
+
result = {
|
|
269
|
+
"start_time": rootproc_data["start_time"],
|
|
270
|
+
"cpu_total" : rootproc_data["cpu_pcent"],
|
|
271
|
+
"cpu_min" : rootproc_data["cpu_pcent"],
|
|
272
|
+
"cpu_max" : rootproc_data["cpu_pcent"],
|
|
273
|
+
"pmemory_total" : rootproc_data["pmemory"],
|
|
274
|
+
"pmemory_min" : rootproc_data["pmemory"],
|
|
275
|
+
"pmemory_max" : rootproc_data["pmemory"],
|
|
276
|
+
"vmemory_total" : rootproc_data["vmemory"],
|
|
277
|
+
"vmemory_min" : rootproc_data["vmemory"],
|
|
278
|
+
"vmemory_max" : rootproc_data["vmemory"],
|
|
279
|
+
"processes" : 1
|
|
280
|
+
}
|
|
281
|
+
if perprocess:
|
|
282
|
+
result["process"] = rootproc_data
|
|
283
|
+
result["process"]["children"] = []
|
|
284
|
+
|
|
285
|
+
processes = [(rootproc.children(),result["process"]["children"] if perprocess else None)]
|
|
286
|
+
while processes:
|
|
287
|
+
childproces,childrendatas = processes.pop(0)
|
|
288
|
+
for childproc in childproces:
|
|
289
|
+
childproc_data = get_process_healthdata(childproc)
|
|
290
|
+
|
|
291
|
+
result["cpu_total"] += childproc_data["cpu_pcent"]
|
|
292
|
+
if result["cpu_min"] > childproc_data["cpu_pcent"]:
|
|
293
|
+
result["cpu_min"] = childproc_data["cpu_pcent"]
|
|
294
|
+
if result["cpu_max"] < childproc_data["cpu_pcent"]:
|
|
295
|
+
result["cpu_max"] = childproc_data["cpu_pcent"]
|
|
296
|
+
|
|
297
|
+
result["pmemory_total"] += childproc_data["pmemory"]
|
|
298
|
+
if result["pmemory_min"] > childproc_data["pmemory"]:
|
|
299
|
+
result["pmemory_min"] = childproc_data["pmemory"]
|
|
300
|
+
if result["pmemory_max"] < childproc_data["pmemory"]:
|
|
301
|
+
result["pmemory_max"] = childproc_data["pmemory"]
|
|
302
|
+
|
|
303
|
+
result["vmemory_total"] += childproc_data["vmemory"]
|
|
304
|
+
if result["vmemory_min"] > childproc_data["vmemory"]:
|
|
305
|
+
result["vmemory_min"] = childproc_data["vmemory"]
|
|
306
|
+
if result["vmemory_max"] < childproc_data["vmemory"]:
|
|
307
|
+
result["vmemory_max"] = childproc_data["vmemory"]
|
|
308
|
+
|
|
309
|
+
result["processes"] += 1
|
|
310
|
+
|
|
311
|
+
if perprocess:
|
|
312
|
+
childrendatas.append(childproc_data)
|
|
313
|
+
|
|
314
|
+
childproc_children = childproc.children()
|
|
315
|
+
if childproc_children:
|
|
316
|
+
if perprocess:
|
|
317
|
+
childproc_data["children"] = []
|
|
318
|
+
processes.append((childproc_children,childproc_data["children"] if perprocess else None))
|
|
319
|
+
|
|
320
|
+
return result
|
|
321
|
+
|
|
322
|
+
def get_workload_healthdata():
|
|
291
323
|
|
|
292
|
-
processesdata = []
|
|
293
|
-
for line in result.stdout.split("\n"):
|
|
294
|
-
line = line.strip()
|
|
295
|
-
if not line:
|
|
296
|
-
continue
|
|
297
|
-
if any(c in line for c in GET_RESOURCEUSAGE_PIPECMDS):
|
|
298
|
-
continue
|
|
299
|
-
data = line.split(maxsplit=3)
|
|
300
|
-
data[0] = float(data[0])
|
|
301
|
-
data[1] = float(data[1]) / 1024
|
|
302
|
-
data[2] = float(data[2]) / 1024
|
|
303
|
-
del data[3]
|
|
304
|
-
processesdata.append(data)
|
|
305
|
-
|
|
306
|
-
#populate the resource data
|
|
307
324
|
result = {
|
|
308
|
-
"
|
|
309
|
-
"total_vmemory":0,
|
|
310
|
-
"total_pmemory":0,
|
|
311
|
-
"processes":0,
|
|
312
|
-
"min_cpu":None,
|
|
313
|
-
"max_cpu":None,
|
|
314
|
-
"min_vmemory":None,
|
|
315
|
-
"max_vmemory":None,
|
|
316
|
-
"min_pmemory":None,
|
|
317
|
-
"max_pmemory":None
|
|
325
|
+
"resources": get_workload_app_healthdata(HEALTHCHECK_PROCESSDATA_ENABLED)
|
|
318
326
|
}
|
|
319
|
-
|
|
320
|
-
result["
|
|
321
|
-
result["total_vmemory"] += data[1]
|
|
322
|
-
result["total_pmemory"] += data[2]
|
|
323
|
-
result["processes"] += 1
|
|
324
|
-
|
|
325
|
-
if result["min_cpu"] is None or result["min_cpu"] > data[0]:
|
|
326
|
-
result["min_cpu"] = data[0]
|
|
327
|
-
if result["max_cpu"] is None or result["max_cpu"] < data[0]:
|
|
328
|
-
result["max_cpu"] = data[0]
|
|
329
|
-
|
|
330
|
-
if result["min_vmemory"] is None or result["min_vmemory"] > data[1]:
|
|
331
|
-
result["min_vmemory"] = data[1]
|
|
332
|
-
if result["max_vmemory"] is None or result["max_vmemory"] < data[1]:
|
|
333
|
-
result["max_vmemory"] = data[1]
|
|
334
|
-
|
|
335
|
-
if result["min_pmemory"] is None or result["min_pmemory"] > data[2]:
|
|
336
|
-
result["min_pmemory"] = data[2]
|
|
337
|
-
if result["max_pmemory"] is None or result["max_pmemory"] < data[2]:
|
|
338
|
-
result["max_pmemory"] = data[2]
|
|
327
|
+
if HEALTHCHECK_SYSTEMDATA_ENABLED:
|
|
328
|
+
result["system"] = get_workload_system_healthdata()
|
|
339
329
|
|
|
340
330
|
if WORKLOAD_VOLUMES_ENABLED:
|
|
341
|
-
result["volumes"] =
|
|
331
|
+
result["volumes"] = get_volumes_healthdata()
|
|
332
|
+
|
|
342
333
|
return (200,result)
|
|
343
334
|
|
|
344
335
|
bearer_token_re = re.compile("^Bearer\\s+(?P<token>\\S+)\\s*$")
|
|
@@ -446,43 +437,64 @@ def populate_summary_data(datas):
|
|
|
446
437
|
Populate the resource summary data from workloads' resource usage data
|
|
447
438
|
"""
|
|
448
439
|
summary = {
|
|
449
|
-
"
|
|
450
|
-
"
|
|
451
|
-
"
|
|
452
|
-
"
|
|
453
|
-
"
|
|
454
|
-
"
|
|
455
|
-
"
|
|
456
|
-
"
|
|
457
|
-
"
|
|
458
|
-
"
|
|
459
|
-
"
|
|
460
|
-
"
|
|
440
|
+
"cpu_total":0,
|
|
441
|
+
"cpu_min":None,
|
|
442
|
+
"cpu_max":None,
|
|
443
|
+
"process_cpu_min":None,
|
|
444
|
+
"process_cpu_max":None,
|
|
445
|
+
"pmemory_total":0,
|
|
446
|
+
"pmemory_min":None,
|
|
447
|
+
"pmemory_max":None,
|
|
448
|
+
"process_pmemory_min":None,
|
|
449
|
+
"process_pmemory_max":None,
|
|
450
|
+
"vmemory_total":0,
|
|
451
|
+
"vmemory_min":None,
|
|
452
|
+
"vmemory_max":None,
|
|
453
|
+
"process_vmemory_min":None,
|
|
454
|
+
"process_vmemory_max":None,
|
|
455
|
+
"processes_total":0,
|
|
456
|
+
"workloads_running":0,
|
|
457
|
+
"workloads_failed":0,
|
|
461
458
|
}
|
|
462
459
|
for servername,serverdata in datas.items():
|
|
463
460
|
if isinstance(serverdata,str):
|
|
464
|
-
summary["
|
|
461
|
+
summary["workloads_failed"] += 1
|
|
465
462
|
continue
|
|
466
|
-
summary["
|
|
467
|
-
|
|
468
|
-
summary["
|
|
469
|
-
|
|
470
|
-
summary["
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
if summary["
|
|
478
|
-
summary["
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
463
|
+
summary["processes_total"] += serverdata["resources"]["processes"]
|
|
464
|
+
|
|
465
|
+
summary["cpu_total"] += serverdata["resources"]["cpu_total"]
|
|
466
|
+
|
|
467
|
+
summary["cpu_total"] += serverdata["resources"]["cpu_total"]
|
|
468
|
+
if summary["cpu_min"] is None or summary["cpu_min"] > serverdata["resources"]["cpu_total"]:
|
|
469
|
+
summary["cpu_min"] = serverdata["resources"]["cpu_total"]
|
|
470
|
+
if summary["cpu_max"] is None or summary["cpu_max"] < serverdata["resources"]["cpu_total"]:
|
|
471
|
+
summary["cpu_max"] = serverdata["resources"]["cpu_total"]
|
|
472
|
+
if summary["process_cpu_min"] is None or summary["process_cpu_min"] > serverdata["resources"]["cpu_min"]:
|
|
473
|
+
summary["process_cpu_min"] = serverdata["resources"]["cpu_min"]
|
|
474
|
+
if summary["process_cpu_max"] is None or summary["process_cpu_max"] < serverdata["resources"]["cpu_max"]:
|
|
475
|
+
summary["process_cpu_max"] = serverdata["resources"]["cpu_max"]
|
|
476
|
+
|
|
477
|
+
summary["pmemory_total"] += serverdata["resources"]["pmemory_total"]
|
|
478
|
+
if summary["pmemory_min"] is None or summary["pmemory_min"] > serverdata["resources"]["pmemory_total"]:
|
|
479
|
+
summary["pmemory_min"] = serverdata["resources"]["pmemory_total"]
|
|
480
|
+
if summary["pmemory_max"] is None or summary["pmemory_max"] < serverdata["resources"]["pmemory_total"]:
|
|
481
|
+
summary["pmemory_max"] = serverdata["resources"]["pmemory_total"]
|
|
482
|
+
if summary["process_pmemory_min"] is None or summary["process_pmemory_min"] > serverdata["resources"]["pmemory_min"]:
|
|
483
|
+
summary["process_pmemory_min"] = serverdata["resources"]["pmemory_min"]
|
|
484
|
+
if summary["process_pmemory_max"] is None or summary["process_pmemory_max"] < serverdata["resources"]["pmemory_max"]:
|
|
485
|
+
summary["process_pmemory_max"] = serverdata["resources"]["pmemory_max"]
|
|
486
|
+
|
|
487
|
+
summary["vmemory_total"] += serverdata["resources"]["vmemory_total"]
|
|
488
|
+
if summary["vmemory_min"] is None or summary["vmemory_min"] > serverdata["resources"]["vmemory_total"]:
|
|
489
|
+
summary["vmemory_min"] = serverdata["resources"]["vmemory_total"]
|
|
490
|
+
if summary["vmemory_max"] is None or summary["vmemory_max"] < serverdata["resources"]["vmemory_total"]:
|
|
491
|
+
summary["vmemory_max"] = serverdata["resources"]["vmemory_total"]
|
|
492
|
+
if summary["process_vmemory_min"] is None or summary["process_vmemory_min"] > serverdata["resources"]["vmemory_min"]:
|
|
493
|
+
summary["process_vmemory_min"] = serverdata["resources"]["vmemory_min"]
|
|
494
|
+
if summary["process_vmemory_max"] is None or summary["process_vmemory_max"] < serverdata["resources"]["vmemory_max"]:
|
|
495
|
+
summary["process_vmemory_max"] = serverdata["resources"]["vmemory_max"]
|
|
496
|
+
|
|
497
|
+
summary["workloads_running"] += 1
|
|
486
498
|
|
|
487
499
|
datas["summary"] = summary
|
|
488
500
|
|
|
@@ -512,7 +524,7 @@ def harvest_healthdata(request):
|
|
|
512
524
|
if servername == item_version:
|
|
513
525
|
continue
|
|
514
526
|
if servername == registerhostname:
|
|
515
|
-
servers_res[servername] =
|
|
527
|
+
servers_res[servername] = get_workload_healthdata()
|
|
516
528
|
continue
|
|
517
529
|
|
|
518
530
|
serverip,port = serverdata[0]
|
|
@@ -544,24 +556,33 @@ def harvest_healthdata(request):
|
|
|
544
556
|
if serverdata[2] > 0:
|
|
545
557
|
serverdata[2] -= 1
|
|
546
558
|
workloads_changed = True
|
|
547
|
-
|
|
548
|
-
#the server is
|
|
549
|
-
|
|
550
|
-
if
|
|
551
|
-
|
|
559
|
+
elif res.status_code == 599:
|
|
560
|
+
#the server is in good health, add the health data to servers_res
|
|
561
|
+
data = res.json()
|
|
562
|
+
if data["status"] == 401:
|
|
563
|
+
#authentication error, caused by different workload.
|
|
552
564
|
workloads_changed = True
|
|
565
|
+
unreached_servers.append(servername)
|
|
566
|
+
servers_res[servername] = (res.status_code,"{1}:{2},url={0}".format(url,data["status"],data["message"]))
|
|
567
|
+
else:
|
|
568
|
+
servers_res[servername] = (res.status_code,"{1}:{2}. url={0}".format(url,data["status"],data["message"]))
|
|
569
|
+
else:
|
|
570
|
+
#unexpected error, caused by different workload
|
|
571
|
+
workloads_changed = True
|
|
572
|
+
unreached_servers.append(servername)
|
|
573
|
+
servers_res[servername] = (res.status_code,"{1}:{2},url={0}".format(url,res.status_code,res.text))
|
|
553
574
|
|
|
554
575
|
for servername in unreached_servers:
|
|
555
576
|
del workloads[servername]
|
|
556
577
|
|
|
557
|
-
logger.debug("healthdata harvest result :{}".format(servers_res))
|
|
578
|
+
logger.debug("healthdata harvest result :workloads={}, resources={}".format(workloads,servers_res))
|
|
558
579
|
|
|
559
580
|
if workloads_changed:
|
|
560
581
|
save_workloads(workloads,unreached_servers)
|
|
561
582
|
|
|
562
583
|
return (workloads,servers_res)
|
|
563
584
|
|
|
564
|
-
OFFLINE_STATUSCODE_LIST = (502,503,504,-1,-2)
|
|
585
|
+
OFFLINE_STATUSCODE_LIST = (502,503,504,401,403,-1,-2)
|
|
565
586
|
if WORKLOADS > 0 and WORKLOAD_DEPLOYMENT:
|
|
566
587
|
#has a fixed number of workloads and it is a deployment
|
|
567
588
|
WORKLOADNAMES = [get_workloadname(index) for index in range(WORKLOADS)]
|
|
@@ -593,6 +614,14 @@ if WORKLOADS > 0 and WORKLOAD_DEPLOYMENT:
|
|
|
593
614
|
del servers_res[servername]
|
|
594
615
|
|
|
595
616
|
assignedworkloads_changed = False
|
|
617
|
+
if len(WORKLOADNAMES) != len(assignedworkloads):
|
|
618
|
+
for key in [k for k in assignedworkloads.keys()]:
|
|
619
|
+
if key == item_version:
|
|
620
|
+
continue
|
|
621
|
+
if key not in WORKLOADNAMES:
|
|
622
|
+
assignedworkloads_changed = True
|
|
623
|
+
del assignedworkloads[key]
|
|
624
|
+
|
|
596
625
|
if reassign_workloads > 0:
|
|
597
626
|
#Some workloads are not assigned a workload name or are not available
|
|
598
627
|
#Using the following to replace the exisint one with new one if possible
|
|
@@ -691,24 +720,27 @@ else:
|
|
|
691
720
|
|
|
692
721
|
def workload_healthdata_view(request):
|
|
693
722
|
global secret
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
723
|
+
try:
|
|
724
|
+
token = get_auth_bearer(request)
|
|
725
|
+
if not token:
|
|
726
|
+
return JsonResponse({"status":401,"message":"Missing access token."},status=599)
|
|
727
|
+
|
|
728
|
+
if not secret or secret != token:
|
|
729
|
+
workloads = cache.get(key_workloads)
|
|
730
|
+
data = workloads.get(registerhostname)
|
|
731
|
+
if data:
|
|
732
|
+
secret = data[1]
|
|
733
|
+
|
|
734
|
+
if secret != token:
|
|
735
|
+
return JsonResponse({"status":401,"message":"Access token doesn't match."},status=599)
|
|
736
|
+
|
|
737
|
+
statuscode,data = get_workload_healthdata()
|
|
738
|
+
if statuscode == 200:
|
|
739
|
+
return JsonResponse(data)
|
|
740
|
+
else:
|
|
741
|
+
return JsonResponse({"status":500,"message":"Server Error"},status=599)
|
|
742
|
+
except Exception as ex:
|
|
743
|
+
return JsonResponse({"status":500,"message":"{}:{}".format(ex.__class__.__name__,str(ex))},status=599)
|
|
712
744
|
|
|
713
745
|
def register_healtcheckurls():
|
|
714
746
|
#Add urls
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|