misc: fix feature.yaml
[vpp.git] / src / scripts / fts.py
1 #!/usr/bin/env python3
2
3 import sys
4 import os
5 import ipaddress
6 import yaml
7 from pprint import pprint
8 import re
9 from jsonschema import validate, exceptions
10 import argparse
11 from subprocess import run, PIPE
12
13 # VPP feature JSON schema
14 schema = {
15     "$schema": "http://json-schema.org/schema#",
16     "type": "object",
17     "properties": {
18         "name": {"type": "string"},
19         "description": {"type": "string"},
20         "maintainer": {"$ref": "#/definitions/maintainers"},
21         "state": {"type": "string",
22                   "enum": ["production", "experimental", "development"]},
23         "features": {"$ref": "#/definitions/features"},
24         "missing": {"$ref": "#/definitions/features"},
25         "properties": {"type": "array",
26                        "items": {"type": "string",
27                                  "enum": ["API", "CLI", "STATS",
28                                           "MULTITHREAD"]},
29                        },
30     },
31     "additionalProperties": False,
32     "definitions": {
33         "maintainers": {
34             "anyof": [{
35                 "type": "array",
36                 "items": {"type": "string"},
37                 "minItems": 1,
38                 },
39                 {"type": "string"}],
40         },
41         "featureobject": {
42             "type": "object",
43             "patternProperties": {
44                 "^.*$": {"$ref": "#/definitions/features"},
45             },
46         },
47         "features": {
48             "type": "array",
49             "items": {"anyOf": [{"$ref": "#/definitions/featureobject"},
50                                 {"type": "string"},
51                                 ]},
52             "minItems": 1,
53         },
54     },
55 }
56
57
58 def filelist_from_git_status():
59     filelist = []
60     git_status = 'git status --porcelain */FEATURE.yaml'
61     rv = run(git_status.split(), stdout=PIPE, stderr=PIPE)
62     if rv.returncode != 0:
63         sys.exit(rv.returncode)
64
65     for l in rv.stdout.decode('ascii').split('\n'):
66         if len(l):
67             filelist.append(l.split()[1])
68     return filelist
69
70
71 def filelist_from_git_ls():
72     filelist = []
73     git_ls = 'git ls-files :(top)*/FEATURE.yaml'
74     rv = run(git_ls.split(), stdout=PIPE, stderr=PIPE)
75     if rv.returncode != 0:
76         sys.exit(rv.returncode)
77
78     for l in rv.stdout.decode('ascii').split('\n'):
79         if len(l):
80             filelist.append(l)
81     return filelist
82
83
84 def output_features(indent, fl):
85     for f in fl:
86         if type(f) is dict:
87             for k, v in f.items():
88                 print('{}- {}'.format(' ' * indent, k))
89                 output_features(indent + 2, v)
90         else:
91             print('{}- {}'.format(' ' * indent, f))
92
93
94 def output_markdown(features):
95     for k, v in features.items():
96         print('# {}'.format(v['name']))
97         if type(v['maintainer']) is list:
98             print('Maintainers: ' +
99                   ', '.join('{}'.format(m) for m in
100                             v['maintainer']) + '  ')
101         else:
102             print('Maintainer: {}  '.format(v['maintainer']))
103         print('State: {}  \n'.format(v['state']))
104         print('{}\n'.format(v['description']))
105         output_features(0, v['features'])
106         if 'missing' in v:
107             print('\n## Missing')
108             output_features(0, v['missing'])
109         print()
110
111
112 def main():
113     parser = argparse.ArgumentParser(description='VPP Feature List.')
114     parser.add_argument('--validate', dest='validate', action='store_true',
115                         help='validate the FEATURE.yaml file')
116     parser.add_argument('--git-status', dest='git_status', action='store_true',
117                         help='Get filelist from git status')
118     parser.add_argument('--all', dest='all', action='store_true',
119                         help='Validate all files in repository')
120     parser.add_argument('--markdown', dest='markdown', action='store_true',
121                         help='Output feature table in markdown')
122     parser.add_argument('infile', nargs='?', type=argparse.FileType('r'),
123                         default=sys.stdin)
124     args = parser.parse_args()
125
126     features = {}
127
128     if args.git_status:
129         filelist = filelist_from_git_status()
130     elif args.all:
131         filelist = filelist_from_git_ls()
132     else:
133         filelist = args.infile
134
135     for featurefile in filelist:
136         featurefile = featurefile.rstrip()
137
138         # Load configuration file
139         with open(featurefile) as f:
140             cfg = yaml.load(f, Loader=yaml.SafeLoader)
141         try:
142             validate(instance=cfg, schema=schema)
143         except exceptions.ValidationError:
144             print('File does not validate: {}'.format(featurefile),
145                   file=sys.stderr)
146             raise
147         features[featurefile] = cfg
148
149     if args.markdown:
150         output_markdown(features)
151
152
153 if __name__ == '__main__':
154     main()