7 from pprint import pprint
9 from jsonschema import validate, exceptions
11 from subprocess import run, PIPE
12 from io import StringIO
14 # VPP feature JSON schema
16 "$schema": "http://json-schema.org/schema#",
19 "name": {"type": "string"},
20 "description": {"type": "string"},
21 "maintainer": {"$ref": "#/definitions/maintainers"},
22 "state": {"type": "string",
23 "enum": ["production", "experimental", "development"]},
24 "features": {"$ref": "#/definitions/features"},
25 "missing": {"$ref": "#/definitions/features"},
26 "properties": {"type": "array",
27 "items": {"type": "string",
28 "enum": ["API", "CLI", "STATS",
32 "additionalProperties": False,
37 "items": {"type": "string"},
44 "patternProperties": {
45 "^.*$": {"$ref": "#/definitions/features"},
50 "items": {"anyOf": [{"$ref": "#/definitions/featureobject"},
59 def filelist_from_git_status():
61 git_status = 'git status --porcelain */FEATURE.yaml'
62 rv = run(git_status.split(), stdout=PIPE, stderr=PIPE)
63 if rv.returncode != 0:
64 sys.exit(rv.returncode)
66 for l in rv.stdout.decode('ascii').split('\n'):
68 filelist.append(l.split()[1])
72 def filelist_from_git_ls():
74 git_ls = 'git ls-files :(top)*/FEATURE.yaml'
75 rv = run(git_ls.split(), stdout=PIPE, stderr=PIPE)
76 if rv.returncode != 0:
77 sys.exit(rv.returncode)
79 for l in rv.stdout.decode('ascii').split('\n'):
84 def version_from_git():
85 git_describe = 'git describe'
86 rv = run(git_describe.split(), stdout=PIPE, stderr=PIPE)
87 if rv.returncode != 0:
88 sys.exit(rv.returncode)
89 return rv.stdout.decode('ascii').split('\n')[0]
94 def __init__(self, stream):
98 def print_maintainer(self, o):
99 write = self.stream.write
101 write('Maintainers: ' +
102 ', '.join(f'{m}' for m in
105 write(f'Maintainer: {o} \n')
107 _dispatch['maintainer'] = print_maintainer
109 def print_features(self, o, indent=0):
110 write = self.stream.write
112 indentstr = ' ' * indent
114 for k, v in f.items():
115 write(f'{indentstr}- {k}\n')
116 self.print_features(v, indent + 2)
118 write(f'{indentstr}- {f}\n')
120 _dispatch['features'] = print_features
122 def print_markdown_header(self, o):
123 write = self.stream.write
125 version = version_from_git()
126 write(f'VPP version: {version}\n\n')
127 _dispatch['markdown_header'] = print_markdown_header
129 def print_name(self, o):
130 write = self.stream.write
133 _dispatch['name'] = print_name
135 def print_description(self, o):
136 write = self.stream.write
138 _dispatch['description'] = print_description
140 def print_state(self, o):
141 write = self.stream.write
142 write(f'Feature maturity level: {o} \n')
143 _dispatch['state'] = print_state
145 def print_properties(self, o):
146 write = self.stream.write
147 write(f'Supports: {" ".join(o)} \n')
148 _dispatch['properties'] = print_properties
150 def print_missing(self, o):
151 write = self.stream.write
152 write('\nNot yet implemented: \n')
153 self.print_features(o)
154 _dispatch['missing'] = print_missing
156 def print_code(self, o):
157 write = self.stream.write
158 write(f'Source Code: [{o}]({o}) \n')
159 _dispatch['code'] = print_code
161 def print(self, t, o):
162 write = self.stream.write
163 if t in self._dispatch:
164 self._dispatch[t](self, o,)
166 write('NOT IMPLEMENTED: {t}\n')
168 def output_toc(toc, stream):
170 write('## VPP Feature list:\n')
173 ref = t.lower().replace(' ', '-')
174 write(f'[{t}](#{ref}) \n')
179 def featurelistsort(k):
190 return orderedfields[k[0]]
192 def output_markdown(features, fields, notfields):
195 m.print('markdown_header', 'Feature Details:')
196 for path, featuredef in sorted(features.items(), key=featuresort):
197 codeurl = 'https://git.fd.io/vpp/tree/src/' + '/'.join(os.path.normpath(path).split('/')[1:-1])
198 featuredef['code'] = codeurl
199 for k, v in sorted(featuredef.items(), key=featurelistsort):
201 if k not in notfields:
209 tocstream = StringIO()
210 output_toc(m.toc, tocstream)
211 return tocstream, stream
214 parser = argparse.ArgumentParser(description='VPP Feature List.')
215 parser.add_argument('--validate', dest='validate', action='store_true',
216 help='validate the FEATURE.yaml file')
217 parser.add_argument('--git-status', dest='git_status', action='store_true',
218 help='Get filelist from git status')
219 parser.add_argument('--all', dest='all', action='store_true',
220 help='Validate all files in repository')
221 parser.add_argument('--markdown', dest='markdown', action='store_true',
222 help='Output feature table in markdown')
223 parser.add_argument('infile', nargs='?', type=argparse.FileType('r'),
225 group = parser.add_mutually_exclusive_group()
226 group.add_argument('--include', help='List of fields to include')
227 group.add_argument('--exclude', help='List of fields to exclude')
228 args = parser.parse_args()
232 filelist = filelist_from_git_status()
234 filelist = filelist_from_git_ls()
236 filelist = args.infile
239 fields = args.include.split(',')
243 notfields = args.exclude.split(',')
247 for featurefile in filelist:
248 featurefile = featurefile.rstrip()
250 # Load configuration file
251 with open(featurefile) as f:
252 cfg = yaml.load(f, Loader=yaml.SafeLoader)
254 validate(instance=cfg, schema=schema)
255 except exceptions.ValidationError:
256 print('File does not validate: {featurefile}',
259 features[featurefile] = cfg
263 tocstream, stream = output_markdown(features, fields, notfields)
264 print(tocstream.getvalue())
265 print(stream.getvalue())
269 if __name__ == '__main__':