Mercurial > hg > freeDiameter
comparison contrib/tools/csv_to_fd @ 1463:8f6c77f24b1a
csv_to_fd: add QoSFilterRule. style fixes
Support derived type QoSFilterRule from RFC 7155 section 4.1.1
Minor code refactor.
Expand comments, removing UTF-8 chars and encoding requirement.
author | Luke Mewburn <luke@mewburn.net> |
---|---|
date | Mon, 09 Mar 2020 22:28:04 +1100 |
parents | a86eb3375b95 |
children | 1404de313b85 |
comparison
equal
deleted
inserted
replaced
1462:b573eda8f4a2 | 1463:8f6c77f24b1a |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # vim: set fileencoding=utf-8 : | |
3 | 2 |
4 """ | 3 """ |
5 Convert CSV files containing RADIUS or Diameter AVP tables | 4 Convert CSV files containing RADIUS or Diameter AVP tables |
6 into various formats. | 5 into various formats. |
7 | 6 |
8 Format of the CSV files is one of: | 7 Format of the CSV files is one of: |
9 - Row per 3GPP AVP tables: | 8 - Row per 3GPP AVP tables: |
10 Name, Code, Section, DataType, Must, May, ShouldNot, MustNot [, ...] | 9 Name, Code, Section, DataType, Must, May, ShouldNot, MustNot [, ...] |
11 - Name: | 10 - Name: |
12 AVP Name. String, validated as ALPHA *(ALPHA / DIGIT / "-") | 11 AVP Name. String, validated as ALPHA *(ALPHA / DIGIT / "-") |
12 per RFC 6733 section 3.2. | |
13 - Code: | 13 - Code: |
14 AVP Code. Integer, 0..4294967295. | 14 AVP Code. Integer, 0..4294967295. |
15 - Section: | 15 - Section: |
16 Section in relevant standard. String. | 16 Section in relevant standard. String. |
17 - DataType: | 17 - DataType: |
18 AVP Data Type. String, validated per Per RFC 6733 § 4.2 and § 4.3. | 18 AVP Data Type. String, validated per basic and derived types in: |
19 - RFC 6733 section 4.2 | |
20 - RFC 6733 section 4.3 | |
21 - RFC 7155 section 4.1 | |
19 - Must, May, ShouldNot, MustNot: | 22 - Must, May, ShouldNot, MustNot: |
20 Flags, possibly comma or space separated: M, V | 23 Flags, possibly comma or space separated: M, V |
21 | 24 |
22 - Comment row. First cell: | 25 - Comment row. First cell: |
23 # Comment text Comment text | 26 # Comment text Comment text |
53 'shouldnot', | 56 'shouldnot', |
54 'mustnot', | 57 'mustnot', |
55 'encrypt', | 58 'encrypt', |
56 ] | 59 ] |
57 | 60 |
61 DERIVED_TO_BASE = { | |
62 'Address': 'OctetString', # RFC 6733 section 4.3.1 | |
63 'Time': 'OctetString', # RFC 6733 section 4.3.1 | |
64 'UTF8String': 'OctetString', # RFC 6733 section 4.3.1 | |
65 'DiameterIdentity': 'OctetString', # RFC 6733 section 4.3.1 | |
66 'DiameterURI': 'OctetString', # RFC 6733 section 4.3.1 | |
67 'Enumerated': 'Integer32', # RFC 6733 section 4.3.1 | |
68 'IPFilterRule': 'OctetString', # RFC 6733 section 4.3.1 | |
69 'QoSFilterRule': 'OctetString', # RFC 7155 section 4.1.1 | |
70 } | |
71 | |
72 # See https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers | |
58 VENDOR_TO_NAME = { | 73 VENDOR_TO_NAME = { |
59 0: '', | 74 0: '', |
60 193: 'Ericsson', | 75 193: 'Ericsson', |
61 8164: 'Starent', | 76 8164: 'Starent', |
62 10415: '3GPP', | 77 10415: '3GPP', |
64 | 79 |
65 | 80 |
66 class Avp(object): | 81 class Avp(object): |
67 """Store an AVP row.""" | 82 """Store an AVP row.""" |
68 | 83 |
69 # Regex to validate avp-name per RFC 6733 § 3.2, | 84 # Regex to validate avp-name per RFC 6733 section 3.2, |
70 # with changes: | 85 # with changes: |
71 # - Allow avp-name to start with numbers (for 3GPP) | 86 # - Allow avp-name to start with numbers (for 3GPP) |
72 # - Allow '.' in avp-name, for existing dict_dcca_3gpp usage. | 87 # - Allow '.' in avp-name, for existing dict_dcca_3gpp usage. |
73 # TODO: if starts with digit, ensure contains a letter somewhere? | 88 # TODO: if starts with digit, ensure contains a letter somewhere? |
74 _name_re = re.compile(r'^[a-zA-Z0-9][a-zA-Z0-9-\.]*$') | 89 _name_re = re.compile(r'^[a-zA-Z0-9][a-zA-Z0-9-\.]*$') |
97 # Validate CSV fields | 112 # Validate CSV fields |
98 if not self._name_re.match(self.name): | 113 if not self._name_re.match(self.name): |
99 raise ValueError('Invalid AVP name "{}"'.format(self.name)) | 114 raise ValueError('Invalid AVP name "{}"'.format(self.name)) |
100 if (self.code < 0 or self.code > 4294967295): | 115 if (self.code < 0 or self.code > 4294967295): |
101 raise ValueError('Invalid AVP code {}'.format(self.code)) | 116 raise ValueError('Invalid AVP code {}'.format(self.code)) |
102 if self.datatype not in ( | 117 if (self.datatype not in ( |
103 'OctetString', 'Integer32', 'Integer64', 'Unsigned32', | 118 'OctetString', 'Integer32', 'Integer64', 'Unsigned32', |
104 'Unsigned64', 'Float32', 'Float64', 'Grouped', | 119 'Unsigned64', 'Float32', 'Float64', 'Grouped') |
105 'Address', 'Time', 'UTF8String', 'DiameterIdentity', | 120 and self.datatype not in DERIVED_TO_BASE): |
106 'DiameterURI', 'Enumerated', 'IPFilterRule', | |
107 ): | |
108 raise ValueError('Invalid AVP data type "{}"'.format( | 121 raise ValueError('Invalid AVP data type "{}"'.format( |
109 self.datatype)) | 122 self.datatype)) |
110 # TODO: validate must, may, shouldnot, mustnot | 123 # TODO: validate must, may, shouldnot, mustnot |
111 | 124 |
112 @property | 125 @property |
199 #= /*==============*/ | 212 #= /*==============*/ |
200 # [blank line] | 213 # [blank line] |
201 """ | 214 """ |
202 | 215 |
203 COMMENT_WIDTH = 64 | 216 COMMENT_WIDTH = 64 |
204 | |
205 DERIVED_TO_BASE = { | |
206 'Address': 'OctetString', | |
207 'Time': 'OctetString', | |
208 'UTF8String': 'OctetString', | |
209 'DiameterIdentity': 'OctetString', | |
210 'DiameterURI': 'OctetString', | |
211 'Enumerated': 'Integer32', | |
212 'IPFilterRule': 'OctetString', | |
213 } | |
214 | 217 |
215 def __init__(self): | 218 def __init__(self): |
216 self.lines = [] | 219 self.lines = [] |
217 | 220 |
218 def next_file(self, filename): | 221 def next_file(self, filename): |
233 self.build_flags(', '.join([avp.must, avp.mustnot])))) | 236 self.build_flags(', '.join([avp.must, avp.mustnot])))) |
234 self.add('\t\t\t{},\t/* Fixed flag values */'.format( | 237 self.add('\t\t\t{},\t/* Fixed flag values */'.format( |
235 self.build_flags(avp.must))) | 238 self.build_flags(avp.must))) |
236 # TODO: add trailing comma? | 239 # TODO: add trailing comma? |
237 self.add('\t\t\tAVP_TYPE_{}\t/* base type of data */'.format( | 240 self.add('\t\t\tAVP_TYPE_{}\t/* base type of data */'.format( |
238 self.DERIVED_TO_BASE.get( | 241 DERIVED_TO_BASE.get(avp.datatype, avp.datatype).upper())) |
239 avp.datatype, avp.datatype).upper())) | |
240 self.add('\t\t};') | 242 self.add('\t\t};') |
241 avp_type = 'NULL' | 243 avp_type = 'NULL' |
242 if 'Enumerated' == avp.datatype: | 244 if 'Enumerated' == avp.datatype: |
243 self.add('\t\tstruct dict_object\t*type;') | 245 self.add('\t\tstruct dict_object\t*type;') |
244 vendor_prefix = '' | 246 vendor_prefix = '' |
249 '"Enumerated({prefix}{name})", NULL, NULL, NULL }};'.format( | 251 '"Enumerated({prefix}{name})", NULL, NULL, NULL }};'.format( |
250 prefix=vendor_prefix, name=avp.name)) | 252 prefix=vendor_prefix, name=avp.name)) |
251 # XXX: add enumerated values | 253 # XXX: add enumerated values |
252 self.add('\t\tCHECK_dict_new(DICT_TYPE, &tdata, NULL, &type);') | 254 self.add('\t\tCHECK_dict_new(DICT_TYPE, &tdata, NULL, &type);') |
253 avp_type = "type" | 255 avp_type = "type" |
254 elif avp.datatype in self.DERIVED_TO_BASE: | 256 elif avp.datatype in DERIVED_TO_BASE: |
255 avp_type = '{}_type'.format(avp.datatype) | 257 avp_type = '{}_type'.format(avp.datatype) |
256 self.add('\t\tCHECK_dict_new(DICT_AVP, &data, {}, NULL);'.format( | 258 self.add('\t\tCHECK_dict_new(DICT_AVP, &data, {}, NULL);'.format( |
257 avp_type)) | 259 avp_type)) |
258 # TODO: remove ; on scope brace | 260 # TODO: remove ; on scope brace |
259 self.add('\t};') | 261 self.add('\t};') |