pymscada 0.2.1__py3-none-any.whl → 0.2.3__py3-none-any.whl

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.

Potentially problematic release.


This version of pymscada might be problematic. Click here for more details.

pymscada/validate.py DELETED
@@ -1,451 +0,0 @@
1
- """Config file validation."""
2
- from cerberus import Validator
3
- import logging
4
- from yaml import dump
5
- from pymscada import Config
6
- from socket import inet_aton
7
-
8
- INT_TAG = {
9
- 'desc': {'type': 'string'},
10
- 'type': {'type': 'string', 'allowed': ['int']},
11
- 'min': {'type': 'integer', 'required': False},
12
- 'max': {'type': 'integer', 'required': False},
13
- 'init': {'type': 'integer', 'required': False},
14
- 'units': {'type': 'string', 'maxlength': 5, 'required': False},
15
- 'format': {'type': 'string', 'allowed': ['date', 'time', 'datetime']}
16
- }
17
- FLOAT_TAG = {
18
- 'desc': {'type': 'string'},
19
- 'type': {'type': 'string', 'allowed': ['float']},
20
- 'min': {'type': 'float', 'required': False},
21
- 'max': {'type': 'float', 'required': False},
22
- 'init': {'type': 'float', 'required': False},
23
- 'units': {'type': 'string', 'maxlength': 5, 'required': False},
24
- 'dp': {'type': 'integer', 'min': 0, 'max': 6, 'required': False},
25
- }
26
- STR_TAG = {
27
- 'desc': {'type': 'string'},
28
- 'type': {'type': 'string', 'allowed': ['str']},
29
- 'init': {'type': 'string', 'required': False},
30
- }
31
- LIST_TAG = {
32
- 'desc': {'type': 'string'},
33
- 'type': {'type': 'string', 'allowed': ['list']},
34
- }
35
- DICT_TAG = {
36
- 'desc': {'type': 'string'},
37
- 'type': {'type': 'string', 'allowed': ['dict']},
38
- 'init': {}
39
- }
40
- MULTI_TAG = {
41
- 'desc': {'type': 'string'},
42
- 'multi': {'type': 'list'},
43
- 'init': {'type': 'integer', 'required': False},
44
- }
45
- BYTES_TAG = {
46
- 'desc': {'type': 'string'},
47
- 'type': {'type': 'string', 'allowed': ['bytes']},
48
- }
49
-
50
- TAG_SCHEMA = {
51
- 'type': 'dict',
52
- 'keysrules': {
53
- 'type': 'string',
54
- # tag name discovery, save for later checking
55
- 'ms_tagname': 'save'
56
- },
57
- 'valuesrules': {
58
- 'type': 'dict',
59
- 'oneof_schema': [
60
- INT_TAG, FLOAT_TAG, STR_TAG, LIST_TAG, DICT_TAG, MULTI_TAG,
61
- BYTES_TAG
62
- ],
63
- # tag type discovery, save for later checking
64
- 'ms_tagtype': True
65
- },
66
- }
67
-
68
- BUS_SCHEMA = {
69
- 'type': 'dict',
70
- 'schema': {
71
- 'ip': {'type': 'string', 'ms_ip': 'ipv4 none'},
72
- 'port': {'type': 'integer', 'min': 1024, 'max': 65536}
73
- }
74
- }
75
-
76
- BRHR_LIST = {
77
- 'type': {'type': 'string', 'allowed': ['br', 'hr']},
78
- }
79
- H123P_LIST = {
80
- 'type': {
81
- 'type': 'string',
82
- 'allowed': ['h1', 'h2', 'h3', 'p'],
83
- },
84
- 'desc': {'type': 'string'}
85
- }
86
- VALUESETFILES_LIST = {
87
- 'type': {
88
- 'type': 'string',
89
- 'allowed': ['value', 'setpoint', 'files'],
90
- },
91
- # tagname must have been found in parsing tags.yaml
92
- 'tagname': {'type': 'string', 'ms_tagname': 'exists'}
93
- }
94
- SELECTDICT_LIST = {
95
- 'type': {'type': 'string', 'allowed': ['selectdict']},
96
- # tagname must have been found in parsing tags.yaml
97
- 'tagname': {'type': 'string', 'ms_tagname': 'exists'},
98
- 'opts': {
99
- 'type': 'dict',
100
- # 'schema': {
101
- # 'type': 'dict',
102
- # # 'schema': {
103
- # # 'type': {'type': 'string', 'required': False},
104
- # # 'multi': {'type': 'list', 'required': False},
105
- # # }
106
- # },
107
- 'required': False
108
- }
109
- }
110
- OPNOTE_LIST = {
111
- 'type': {'type': 'string', 'allowed': ['opnote']},
112
- 'site': {'type': 'list'},
113
- 'by': {'type': 'list'}
114
- }
115
- UPLOT_LIST = {
116
- 'type': {'type': 'string', 'allowed': ['uplot']},
117
- 'ms': {
118
- 'type': 'dict',
119
- 'required': False
120
- },
121
- 'axes': {
122
- 'type': 'list',
123
- 'schema': {
124
- 'type': 'dict',
125
- 'schema': {
126
- 'scale': {'type': 'string'},
127
- 'range': {'type': 'list', 'items': [
128
- {'type': 'float'}, {'type': 'float'}
129
- ]},
130
- 'dp': {'type': 'integer', 'required': False}
131
- }
132
- }
133
- },
134
- 'series': {
135
- 'type': 'list',
136
- 'schema': {
137
- 'type': 'dict',
138
- 'schema': {
139
- # tagname must have been found in parsing tags.yaml
140
- 'tagname': {'type': 'string', 'ms_tagname': 'exists'},
141
- 'label': {'type': 'string', 'required': False},
142
- 'scale': {'type': 'string', 'required': False},
143
- 'color': {'type': 'string', 'required': False},
144
- 'width': {'type': 'float', 'required': False},
145
- 'dp': {'type': 'integer', 'required': False},
146
- }
147
- }
148
- }
149
- }
150
-
151
- LIST_WWWSERVER = {
152
- 'type': 'dict',
153
- 'schema': {
154
- 'name': {'type': 'string'},
155
- 'parent': {'type': 'string', 'nullable': True},
156
- 'items': {
157
- 'type': 'list',
158
- 'schema': {
159
- 'type': 'dict',
160
- 'oneof_schema': [
161
- BRHR_LIST, H123P_LIST, VALUESETFILES_LIST,
162
- SELECTDICT_LIST, OPNOTE_LIST, UPLOT_LIST
163
- ]
164
- }
165
- },
166
- }
167
- }
168
-
169
- WWWSERVER_SCHEMA = {
170
- 'type': 'dict',
171
- 'schema': {
172
- 'bus_ip': {'type': 'string', 'ms_ip': 'none ipv4'},
173
- 'bus_port': {'type': 'integer', 'min': 1024, 'max': 65536},
174
- 'ip': {'type': 'string', 'ms_ip': 'none ipv4'},
175
- 'port': {'type': 'integer', 'min': 1024, 'max': 65536},
176
- 'get_path': {'nullable': True},
177
- 'serve_path': {'nullable': True},
178
- 'paths': {'type': 'list', 'allowed': ['history', 'config', 'pdf']},
179
- 'pages': {
180
- 'type': 'list',
181
- 'schema': LIST_WWWSERVER
182
- }
183
- }
184
- }
185
-
186
- HISTORY_SCHEMA = {
187
- 'type': 'dict',
188
- 'schema': {
189
- 'bus_ip': {'type': 'string', 'ms_ip': 'none ipv4'},
190
- 'bus_port': {'type': 'integer', 'min': 1024, 'max': 65536},
191
- 'path': {'type': 'string'},
192
- }
193
- }
194
-
195
- MODBUSSERVER_SCHEMA = {
196
- 'type': 'dict',
197
- 'schema': {
198
- 'bus_ip': {'type': 'string', 'ms_ip': 'none ipv4', 'nullable': True},
199
- 'bus_port': {'type': 'integer', 'min': 1024, 'max': 65536,
200
- 'nullable': True},
201
- 'path': {'type': 'string'},
202
- 'rtus': {
203
- 'type': 'list',
204
- 'schema': {
205
- 'type': 'dict',
206
- 'schema': {
207
- 'name': {},
208
- 'ip': {},
209
- 'port': {},
210
- 'tcp_udp': {'type': 'string', 'allowed': ['tcp', 'udp']},
211
- 'serve': {
212
- 'type': 'list',
213
- 'schema': {}
214
- }
215
- }
216
- }
217
- },
218
- 'tags': {
219
- 'type': 'dict',
220
- 'keysrules': {
221
- 'type': 'string',
222
- 'ms_tagname': 'none'
223
- },
224
- 'valuesrules': {
225
- 'type': 'dict',
226
- 'schema': {
227
- 'type': {},
228
- 'addr': {}
229
- },
230
- },
231
- },
232
- }
233
- }
234
-
235
- MODBUSCLIENT_SCHEMA = {
236
- 'type': 'dict',
237
- 'schema': {
238
- 'bus_ip': {'type': 'string', 'ms_ip': 'ipv4'},
239
- 'bus_port': {'type': 'integer', 'min': 1024, 'max': 65536,
240
- 'nullable': True},
241
- 'path': {'type': 'string'},
242
- 'rtus': {
243
- 'type': 'list',
244
- 'schema': {
245
- 'type': 'dict',
246
- 'schema': {
247
- 'name': {},
248
- 'ip': {},
249
- 'port': {},
250
- 'tcp_udp': {'type': 'string', 'allowed': ['tcp', 'udp']},
251
- 'rate': {},
252
- 'poll': {
253
- 'type': 'list',
254
- 'schema': {}
255
- }
256
- }
257
- }
258
- },
259
- 'tags': {
260
- 'type': 'dict',
261
- 'keysrules': {
262
- 'type': 'string',
263
- 'ms_tagname': 'exists'
264
- },
265
- 'valuesrules': {
266
- 'type': 'dict',
267
- 'schema': {
268
- 'type': {},
269
- 'read': {},
270
- 'write': {}
271
- }
272
- }
273
- }
274
- }
275
- }
276
-
277
- SNMPCLIENT_SCHEMA = {
278
- 'type': 'dict',
279
- 'schema': {
280
- 'bus_ip': {'type': 'string', 'ms_ip': 'ipv4'},
281
- 'bus_port': {'type': 'integer', 'min': 1024, 'max': 65536,
282
- 'nullable': True},
283
- 'path': {'type': 'string'},
284
- 'rtus': {
285
- 'type': 'list',
286
- 'schema': {
287
- 'type': 'dict',
288
- 'schema': {
289
- 'name': {},
290
- 'ip': {},
291
- 'community': {},
292
- 'rate': {},
293
- 'poll': {
294
- 'type': 'list',
295
- 'schema': {}
296
- }
297
- }
298
- }
299
- },
300
- 'tags': {
301
- 'type': 'dict',
302
- 'keysrules': {
303
- 'type': 'string',
304
- 'ms_tagname': 'exists'
305
- },
306
- 'valuesrules': {
307
- 'type': 'dict',
308
- 'schema': {
309
- 'type': {},
310
- 'read': {}
311
- }
312
- }
313
- }
314
- }
315
- }
316
-
317
- LOGIXCLIENT_SCHEMA = {
318
- 'type': 'dict',
319
- 'schema': {
320
- 'bus_ip': {'type': 'string', 'ms_ip': 'ipv4'},
321
- 'bus_port': {'type': 'integer', 'min': 1024, 'max': 65536,
322
- 'nullable': True},
323
- 'path': {'type': 'string'},
324
- 'rtus': {
325
- 'type': 'list',
326
- 'schema': {
327
- 'type': 'dict',
328
- 'schema': {
329
- 'name': {},
330
- 'ip': {},
331
- 'rate': {},
332
- 'poll': {
333
- 'type': 'list',
334
- 'schema': {}
335
- }
336
- }
337
- }
338
- },
339
- 'tags': {
340
- 'type': 'dict',
341
- 'keysrules': {
342
- 'type': 'string',
343
- 'ms_tagname': 'exists'
344
- },
345
- 'valuesrules': {
346
- 'type': 'dict',
347
- 'schema': {
348
- 'type': {},
349
- 'read': {},
350
- 'write': {}
351
- }
352
- }
353
- }
354
- }
355
- }
356
-
357
-
358
- class MsValidator(Validator):
359
- """Additional application checks."""
360
-
361
- ms_tagnames = {}
362
- ms_notagcheck = {}
363
-
364
- def __init__(self, *args, **kwargs):
365
- super().__init__(*args, **kwargs)
366
- self.context = {
367
- 'current_file': None,
368
- 'current_section': None
369
- }
370
-
371
- def _validate_ms_tagname(self, constraint, field, value):
372
- """
373
- Test tagname exists, capture when true.
374
-
375
- The rule's arguments are validated against this schema:
376
- {'type': 'string'}
377
- """
378
- if '.' in field:
379
- self._error(field, f"'.' invalid in tag definition in {self.context['current_file']}")
380
- if constraint == 'save':
381
- if field in self.ms_tagnames:
382
- self._error(field, f"attempt to redefine in {self.context['current_file']}")
383
- else:
384
- self.ms_tagnames[field] = {'type': None}
385
- elif constraint == 'exists':
386
- if value not in self.ms_tagnames:
387
- self._error(field, f"tag '{value}' was not defined in tags.yaml")
388
- elif constraint == 'none':
389
- pass
390
- else:
391
- pass
392
-
393
- def _validate_ms_tagtype(self, constraint, field, value):
394
- """
395
- Test tagname type, capture when true.
396
-
397
- The rule's arguments are validated against this schema:
398
- {'type': 'boolean'}
399
- """
400
- if constraint and field in self.ms_tagnames:
401
- if self.ms_tagnames[field]['type'] is None:
402
- if 'multi' in value:
403
- self.ms_tagnames[field]['type'] = 'int'
404
- else:
405
- self.ms_tagnames[field]['type'] = value['type']
406
- else:
407
- self._error(field, 'attempt to redefine type')
408
- else:
409
- pass
410
-
411
- def _validate_ms_ip(self, constraint, field, value):
412
- """
413
- Test session.inet_aton works for the address.
414
-
415
- The rule's arguments are validated against this schema:
416
- {'type': 'string'}
417
- """
418
- if value is None and 'none' in constraint:
419
- pass
420
- elif 'ipv4' in constraint:
421
- try:
422
- inet_aton(value)
423
- except (OSError, TypeError):
424
- self._error(field, 'ip address fails socket.inet_aton')
425
-
426
-
427
- def validate(path: str = None):
428
- """Validate."""
429
- s = {
430
- 'tags': TAG_SCHEMA,
431
- 'bus': BUS_SCHEMA,
432
- 'wwwserver': WWWSERVER_SCHEMA,
433
- 'history': HISTORY_SCHEMA,
434
- 'modbusserver': MODBUSSERVER_SCHEMA,
435
- 'modbusclient': MODBUSCLIENT_SCHEMA,
436
- 'snmpclient': SNMPCLIENT_SCHEMA,
437
- 'logixclient': LOGIXCLIENT_SCHEMA,
438
- }
439
- v = MsValidator(s)
440
- c = {}
441
- prefix = './' if path is None else f"{path}/"
442
- for name in s.keys():
443
- try:
444
- v.context['current_file'] = f'{name}.yaml'
445
- v.context['current_section'] = name
446
- c[name] = dict(Config(f'{prefix}{name}.yaml'))
447
- except Exception as e:
448
- v._error(name, f'Failed to load {name}.yaml: {str(e)}')
449
- return False, dump(v.errors), prefix
450
- res = v.validate(c)
451
- return res, dump(v.errors), prefix