summaryrefslogtreecommitdiffstats
path: root/makeapi
diff options
context:
space:
mode:
Diffstat (limited to 'makeapi')
-rwxr-xr-xmakeapi238
1 files changed, 238 insertions, 0 deletions
diff --git a/makeapi b/makeapi
new file mode 100755
index 000000000..30eac18c3
--- /dev/null
+++ b/makeapi
@@ -0,0 +1,238 @@
+#!/usr/bin/env python
+# Authors:
+# Rob Crittenden <rcritten@redhat.com>
+#
+# Copyright (C) 2011 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Test the API against a known-good API to ensure that changes aren't made
+# lightly.
+
+import sys
+import os
+import re
+from ipalib import *
+from ipalib.text import Gettext
+
+API_FILE='API.txt'
+
+API_FILE_DIFFERENCE = 1
+API_NEW_COMMAND = 2
+API_NO_FILE = 4
+
+def parse_options():
+ from optparse import OptionParser
+
+ parser = OptionParser()
+ parser.add_option("--validate", dest="validate", action="store_true",
+ default=False, help="Validate the API vs the stored API")
+
+ options, args = parser.parse_args()
+ return options, args
+
+def strip_doc(line):
+ """
+ Remove the doc= line from the repr() of a Paramter.
+ """
+ s = line.find(' doc=')
+ if s >= 0:
+ e = line.find('), ', s)
+ line = '%s%s' % (line[0:s], line[e+2:])
+
+ return line
+
+def make_api():
+ """
+ Write a new API file from the current tree.
+ """
+ fd = open(API_FILE, 'w')
+ for cmd in api.Command():
+ fd.write('command: %s\n' % cmd.name)
+ fd.write('args: %d,%d,%d\n' % (len(cmd.args), len(cmd.options), len(cmd.output)))
+ for a in cmd.args():
+ fd.write('arg: %s\n' % strip_doc(repr(a)))
+ for o in cmd.options():
+ fd.write('option: %s\n' % strip_doc(repr(o)))
+ for o in cmd.output():
+ fd.write('output: %s\n' % strip_doc(repr(o)))
+ fd.close()
+
+ return 0
+
+def find_name(line):
+ """
+ Break apart a Param line and pull out the name. It would be nice if we
+ could just eval() the line but we wouldn't have defined any validators
+ or normalizers it may be using.
+ """
+ m = re.match('^[a-zA-Z0-9]+\(\'([a-z][_a-z0-9?\*\+]*)\'.*', line)
+ if m:
+ name = m.group(1)
+ else:
+ print "Couldn't find name in: %s" % line
+ name = ''
+ return name
+
+def validate_api():
+ """
+ Compare the API in the file to the one in ipalib.
+
+ Return a bitwise return code to identify the types of errors found, if
+ any.
+ """
+ fd = open(API_FILE, 'r')
+ lines = fd.readlines()
+ fd.close()
+
+ rval = 0
+
+ # First run through the file and compare it to the API
+ existing_cmds = []
+ cmd = None
+ for line in lines:
+ line = line.strip()
+ if line.startswith('command:'):
+ if cmd:
+ # Check the args of the previous command.
+ if found_args != expected_args:
+ print 'Argument count in %s of %d doesn\'t match expected: %d' % (
+ name, found_args, expected_args)
+ rval |= API_FILE_DIFFERENCE
+ if found_options != expected_options:
+ print 'Options count in %s of %d doesn\'t match expected: %d' % (
+ name, found_options, expected_options)
+ rval |= API_FILE_DIFFERENCE
+ if found_output != expected_output:
+ print 'Output count in %s of %d doesn\'t match expected: %d' % (
+ name, found_output, expected_output)
+ rval |= API_FILE_DIFFERENCE
+ (arg, name) = line.split(': ', 1)
+ if name not in api.Command:
+ print "Command %s in API file, not in ipalib" % name
+ rval |= API_FILE_DIFFERENCE
+ cmd = None
+ else:
+ existing_cmds.append(name)
+ cmd = api.Command[name]
+ found_args = 0
+ found_options = 0
+ found_output = 0
+ if line.startswith('args:') and cmd:
+ line = line.replace('args: ', '')
+ (expected_args, expected_options, expected_output) = line.split(',')
+ expected_args = int(expected_args)
+ expected_options = int(expected_options)
+ expected_output = int(expected_output)
+ if line.startswith('arg:') and cmd:
+ line = line.replace('arg: ', '')
+ found = False
+ for a in cmd.args():
+ if strip_doc(repr(a)) == line:
+ found = True
+ else:
+ arg = find_name(line)
+ if a.name == arg:
+ found = True
+ print 'Arg in %s doesn\'t match.\nGot %s\nExpected %s' % (
+ name, strip_doc(repr(a)), line)
+ rval |= API_FILE_DIFFERENCE
+ if found:
+ found_args += 1
+ else:
+ arg = find_name(line)
+ print "Argument '%s' in command '%s' in API file not found" % (arg, name)
+ rval |= API_FILE_DIFFERENCE
+ if line.startswith('option:') and cmd:
+ line = line.replace('option: ', '')
+ found = False
+ for o in cmd.options():
+ if strip_doc(repr(o)) == line:
+ found = True
+ else:
+ option = find_name(line)
+ if o.name == option:
+ found = True
+ print 'Option in %s doesn\'t match. Got %s Expected %s' % (name, o, line)
+ rval |= API_FILE_DIFFERENCE
+ if found:
+ found_options += 1
+ else:
+ option = find_name(line)
+ print "Option '%s' in command '%s' in API file not found" % (option, name)
+ rval |= API_FILE_DIFFERENCE
+ if line.startswith('output:') and cmd:
+ line = line.replace('output: ', '')
+ found = False
+ for o in cmd.output():
+ if strip_doc(repr(o)) == line:
+ found = True
+ else:
+ output = find_name(line)
+ if o.name == output:
+ found = True
+ print 'Output in %s doesn\'t match. Got %s Expected %s' % (name, o, line)
+ rval |= API_FILE_DIFFERENCE
+ if found:
+ found_output += 1
+ else:
+ output = find_name(line)
+ print "Option '%s' in command '%s' in API file not found" % (output, name)
+ rval |= API_FILE_DIFFERENCE
+
+ # Now look for new commands not in the current API
+ for cmd in api.Command():
+ if cmd.name not in existing_cmds:
+ print "Command %s in ipalib, not in API" % cmd.name
+ rval |= API_NEW_COMMAND
+
+ return rval
+
+def main():
+ options, args = parse_options()
+
+ cfg = dict(
+ context='cli',
+ in_server=False,
+ debug=False,
+ verbose=0,
+ validate_api=True,
+ enable_ra=True,
+ )
+
+ api.bootstrap(**cfg)
+ api.finalize()
+
+ if options.validate:
+ if not os.path.exists(API_FILE):
+ print 'No %s to validate' % API_FILE
+ rval = API_NO_FILE
+ else:
+ rval = validate_api()
+ else:
+ print "Writing API to API.txt"
+ rval = make_api()
+
+ if rval & API_FILE_DIFFERENCE:
+ print ''
+ print 'There are one or more changes to the API.\nEither undo the API changes or update API.txt and increment the major version in VERSION.'
+
+ if rval & API_NEW_COMMAND:
+ print ''
+ print 'There are one or more new commands defined.\nUpdate API.txt and increment the minor version in VERSION.'
+
+ return rval
+
+sys.exit(main())