dbca-utils 3.0.3__tar.gz → 3.0.5__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.
Files changed (22) hide show
  1. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/PKG-INFO +21 -1
  2. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/README.md +20 -0
  3. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/pyproject.toml +1 -1
  4. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/src/dbca_utils/healthcheck/healthcheck.py +105 -0
  5. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/LICENSE +0 -0
  6. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/src/dbca_utils/__init__.py +0 -0
  7. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/src/dbca_utils/apps.py +0 -0
  8. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/src/dbca_utils/healthcheck/__init__.py +0 -0
  9. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/src/dbca_utils/healthcheck/urls.py +0 -0
  10. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/src/dbca_utils/middleware.py +0 -0
  11. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/src/dbca_utils/models.py +0 -0
  12. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/src/dbca_utils/utils.py +0 -0
  13. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/tests/__init__.py +0 -0
  14. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/tests/apps.py +0 -0
  15. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/tests/migrations/0001_initial.py +0 -0
  16. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/tests/migrations/__init__.py +0 -0
  17. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/tests/models.py +0 -0
  18. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/tests/settings.py +0 -0
  19. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/tests/templates/tests/test_model_list.html +0 -0
  20. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/tests/tests.py +0 -0
  21. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/tests/urls.py +0 -0
  22. {dbca_utils-3.0.3 → dbca_utils-3.0.5}/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
3
+ Version: 3.0.5
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
@@ -101,3 +101,23 @@ MIDDLEWARE = [
101
101
  - `modifier` - FK to `AUTH_USER_MODEL`, used to record who the object was last modified by
102
102
  - `created` - a timestamp that is set on initial object save
103
103
  - `modified` - an auto-updating timestamp (on each object save)
104
+
105
+ ## Healthcheck feature
106
+ ### Requirements
107
+ - Django 5.2 or later
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 memory data
110
+
111
+ ### Usage
112
+ - Install the app 'dbca_utils' in INSTALLED_APPS
113
+ - Service Configuration
114
+ - HEALTHCHECK_ENABLED: Optional. enable/disable the healthcheck service. default is 'true'
115
+ - PROCESS_FILTER: Optional. find the web app related processes from command 'ps aux'. default is '| grep python'
116
+ - CACHE_PREFIX: Optional. used as the prefix of the cache key. default is ''
117
+ - PORT: Optional. The listening port of the web application. default is '8080'
118
+ - WORKLOADS: Optional. Used if the web app has a fixed replicas.
119
+ - WORKLOAD_DEPLOYMENT: Optional. the workload is deployment if it is true; otherwise it is statefulset. default is 'true'
120
+ - WORKLOAD_FAILED_THRESHOLD: Optional. The number of continuous failed times to treat a pod is offline.
121
+ - Nginx Configuration.
122
+ - Add a location 'location /healthcheck/' and configure it to use basic auth in nginx.
123
+ - Access the url : https://xxx.dbca.wa.gov.au/healthcheck/healthdata to get the health json data
@@ -73,3 +73,23 @@ MIDDLEWARE = [
73
73
  - `modifier` - FK to `AUTH_USER_MODEL`, used to record who the object was last modified by
74
74
  - `created` - a timestamp that is set on initial object save
75
75
  - `modified` - an auto-updating timestamp (on each object save)
76
+
77
+ ## Healthcheck feature
78
+ ### Requirements
79
+ - Django 5.2 or later
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 memory data
82
+
83
+ ### Usage
84
+ - Install the app 'dbca_utils' in INSTALLED_APPS
85
+ - Service Configuration
86
+ - HEALTHCHECK_ENABLED: Optional. enable/disable the healthcheck service. default is 'true'
87
+ - PROCESS_FILTER: Optional. find the web app related processes from command 'ps aux'. default is '| grep python'
88
+ - CACHE_PREFIX: Optional. used as the prefix of the cache key. default is ''
89
+ - PORT: Optional. The listening port of the web application. default is '8080'
90
+ - WORKLOADS: Optional. Used if the web app has a fixed replicas.
91
+ - WORKLOAD_DEPLOYMENT: Optional. the workload is deployment if it is true; otherwise it is statefulset. default is 'true'
92
+ - WORKLOAD_FAILED_THRESHOLD: Optional. The number of continuous failed times to treat a pod is offline.
93
+ - Nginx Configuration.
94
+ - Add a location 'location /healthcheck/' and configure it to use basic auth in nginx.
95
+ - Access the url : https://xxx.dbca.wa.gov.au/healthcheck/healthdata to get the health json data
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dbca-utils"
3
- version = "3.0.3"
3
+ version = "3.0.5"
4
4
  description = "Utilities for DBCA Django apps"
5
5
  authors = [
6
6
  { name = "Rocky Chen", email = "rocky.chen@dbca.wa.gov.au" },
@@ -33,6 +33,18 @@ if WORKLOADS < 0 :
33
33
  WORKLOADS = 0
34
34
  WORKLOAD_FAILED_THRESHOLD = int(os.environ.get("WORKLOAD_FAILED_THRESHOLD",2))
35
35
 
36
+ WORKLOAD_VOLUMES = os.environ.get("WORKLOAD_VOLUMES","automatic")
37
+
38
+ if not WORKLOAD_VOLUMES or WORKLOAD_VOLUMES.lower() in ("disabled","false"):
39
+ WORKLOAD_VOLUMES_ENABLED = False
40
+ WORKLOAD_VOLUMES = None
41
+ elif WORKLOAD_VOLUMES.lower() == "automatic":
42
+ WORKLOAD_VOLUMES_ENABLED = True
43
+ WORKLOAD_VOLUMES = None
44
+ else:
45
+ WORKLOAD_VOLUMES = [v.strip() for v in WORKLOAD_VOLUMES.split(",") if v.strip()]
46
+ WORKLOAD_VOLUMES_ENABLED = True if WORKLOAD_VOLUMES else False
47
+
36
48
 
37
49
  RANDOM_CHARS="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYA0123456789~!@#$%^&*()-_+=`{}[];':\",./<>?"
38
50
  RANDOM_CHARS_MAX_INDEX = len(RANDOM_CHARS) - 1
@@ -104,10 +116,101 @@ def unregister_webappprocess():
104
116
  logger.error("Failed to unregister the webapp process '{}({}).{}'.".format(hostname,ip,pid))
105
117
 
106
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 target.startswith("//"):
153
+ volumes.append(target)
154
+ elif target.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
+
107
209
  item_version = "__version__"
108
210
  key_workloads = "{}__workloads__".format(CACHE_PREFIX)
109
211
  key_workloads_lock = "{}lock__".format(key_workloads)
110
212
 
213
+
111
214
  def register_webappserver(sender,environ,**kwargs):
112
215
  """
113
216
  Register a web server running in the same workload
@@ -234,6 +337,8 @@ def get_workload_healthcheckdata():
234
337
  if result["max_pmemory"] is None or result["max_pmemory"] < data[2]:
235
338
  result["max_pmemory"] = data[2]
236
339
 
340
+ if WORKLOAD_VOLUMES_ENABLED:
341
+ result["volumes"] = get_persistent_volumes_data()
237
342
  return (200,result)
238
343
 
239
344
  bearer_token_re = re.compile("^Bearer\\s+(?P<token>\\S+)\\s*$")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes