blob: 750b73f8dccc8b73c8879d11cea9134a18dafc57 [file] [log] [blame]
Ole Troan6a3064f2019-05-14 13:24:10 +02001#!/usr/bin/env python3
2
3import sys
4import os
5import ipaddress
6import yaml
7from pprint import pprint
8import re
Ole Troanf3aebda2020-01-03 16:37:27 +01009from jsonschema import validate, exceptions
Ole Troan6a3064f2019-05-14 13:24:10 +020010import argparse
11from subprocess import run, PIPE
12
13# VPP feature JSON schema
14schema = {
15 "$schema": "http://json-schema.org/schema#",
16 "type": "object",
17 "properties": {
18 "name": {"type": "string"},
Paul Vinciguerraea1a6512019-11-01 02:34:32 -040019 "description": {"type": "string"},
Ole Troane774a8b2020-01-02 22:32:57 +010020 "maintainer": {"$ref": "#/definitions/maintainers"},
Ole Troan6a3064f2019-05-14 13:24:10 +020021 "state": {"type": "string",
Ole Troane774a8b2020-01-02 22:32:57 +010022 "enum": ["production", "experimental", "development"]},
Paul Vinciguerraea1a6512019-11-01 02:34:32 -040023 "features": {"$ref": "#/definitions/features"},
24 "missing": {"$ref": "#/definitions/features"},
25 "properties": {"type": "array",
26 "items": {"type": "string",
27 "enum": ["API", "CLI", "STATS",
28 "MULTITHREAD"]},
Ole Troan6a3064f2019-05-14 13:24:10 +020029 },
30 },
31 "additionalProperties": False,
32 "definitions": {
Ole Troane774a8b2020-01-02 22:32:57 +010033 "maintainers": {
34 "anyof": [{
35 "type": "array",
36 "items": {"type": "string"},
37 "minItems": 1,
38 },
39 {"type": "string"}],
40 },
Ole Troan6a3064f2019-05-14 13:24:10 +020041 "featureobject": {
42 "type": "object",
43 "patternProperties": {
Paul Vinciguerraea1a6512019-11-01 02:34:32 -040044 "^.*$": {"$ref": "#/definitions/features"},
Ole Troan6a3064f2019-05-14 13:24:10 +020045 },
46 },
47 "features": {
48 "type": "array",
Paul Vinciguerraea1a6512019-11-01 02:34:32 -040049 "items": {"anyOf": [{"$ref": "#/definitions/featureobject"},
50 {"type": "string"},
51 ]},
Ole Troan6a3064f2019-05-14 13:24:10 +020052 "minItems": 1,
53 },
54 },
55}
56
57
Ole Troan6a3064f2019-05-14 13:24:10 +020058def 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
Paul Vinciguerraea1a6512019-11-01 02:34:32 -040070
Ole Troan6a3064f2019-05-14 13:24:10 +020071def 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
Paul Vinciguerraea1a6512019-11-01 02:34:32 -040083
Ole Troan6a3064f2019-05-14 13:24:10 +020084def output_features(indent, fl):
85 for f in fl:
86 if type(f) is dict:
Paul Vinciguerraea1a6512019-11-01 02:34:32 -040087 for k, v in f.items():
Ole Troan6a3064f2019-05-14 13:24:10 +020088 print('{}- {}'.format(' ' * indent, k))
89 output_features(indent + 2, v)
90 else:
91 print('{}- {}'.format(' ' * indent, f))
92
Paul Vinciguerraea1a6512019-11-01 02:34:32 -040093
Ole Troan6a3064f2019-05-14 13:24:10 +020094def output_markdown(features):
Paul Vinciguerraea1a6512019-11-01 02:34:32 -040095 for k, v in features.items():
Ole Troan6a3064f2019-05-14 13:24:10 +020096 print('# {}'.format(v['name']))
Ole Troane774a8b2020-01-02 22:32:57 +010097 if type(v['maintainer']) is list:
98 print('Maintainers: ' +
Ole Troanf3aebda2020-01-03 16:37:27 +010099 ', '.join('{}'.format(m) for m in
100 v['maintainer']) + ' ')
Ole Troane774a8b2020-01-02 22:32:57 +0100101 else:
102 print('Maintainer: {} '.format(v['maintainer']))
Ole Troanf3aebda2020-01-03 16:37:27 +0100103 print('State: {} \n'.format(v['state']))
Ole Troan6a3064f2019-05-14 13:24:10 +0200104 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
Paul Vinciguerraea1a6512019-11-01 02:34:32 -0400111
Ole Troan6a3064f2019-05-14 13:24:10 +0200112def 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:
Paul Vinciguerraea1a6512019-11-01 02:34:32 -0400140 cfg = yaml.load(f, Loader=yaml.SafeLoader)
Ole Troanf3aebda2020-01-03 16:37:27 +0100141 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
Ole Troan6a3064f2019-05-14 13:24:10 +0200147 features[featurefile] = cfg
148
149 if args.markdown:
150 output_markdown(features)
151
Paul Vinciguerraea1a6512019-11-01 02:34:32 -0400152
Ole Troan6a3064f2019-05-14 13:24:10 +0200153if __name__ == '__main__':
154 main()