Giuseppe Lavagetto has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/363808 )
Change subject: [WiP] Add future parser run mode ...................................................................... [WiP] Add future parser run mode Change-Id: I5fc3eabd8704518dfeb17ebeab583010ca774690 --- M puppet_compiler/presentation/html.py M puppet_compiler/puppet.py M puppet_compiler/state.py A puppet_compiler/templates/hostpage.future.jinja2 A puppet_compiler/templates/index.future.jinja2 M puppet_compiler/worker.py 6 files changed, 211 insertions(+), 58 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/operations/software/puppet-compiler refs/changes/08/363808/1 diff --git a/puppet_compiler/presentation/html.py b/puppet_compiler/presentation/html.py index a24a5ba..90f6162 100644 --- a/puppet_compiler/presentation/html.py +++ b/puppet_compiler/presentation/html.py @@ -8,12 +8,24 @@ class Host(object): + tpl = 'hostpage.jinja2' + page_name = 'index.html' def __init__(self, hostname, files, retcode): self.retcode = retcode self.hostname = hostname self.outdir = files.outdir self.diff_file = files.file_for('change', 'diff') + + def _retcode_to_desc(self): + if self.retcode == 'noop': + return 'no change' + elif self.retcode == 'diff': + return 'changes detected' + elif self.retcode == 'error': + return 'change fails' + else: + return 'compiler failure' def htmlpage(self): """ @@ -24,39 +36,52 @@ if self.retcode == 'diff': with open(self.diff_file, 'r') as f: data['diffs'] = f.read() - if self.retcode == 'noop': - data['desc'] = 'no change' - elif self.retcode == 'diff': - data['desc'] = 'changes detected' - elif self.retcode == 'error': - data['desc'] = 'change fails' - else: - data['desc'] = 'compiler failure' - t = env.get_template('hostpage.jinja2') + data['desc'] = self._retcode_to_desc() + t = env.get_template(self.tpl) page = t.render(host=self.hostname, jid=job_id, chid=change_id, **data) - with open(os.path.join(self.outdir, 'index.html'), 'w') as f: + with open(os.path.join(self.outdir, self.page_name), 'w') as f: f.write(page) -class Index(object): - default_mode = 'change' +class FutureHost(Host): + tpl = 'hostpage.future.jinja2' + page_name = 'index-future.html' - def __init__(self, outdir, mode): - if mode == self.default_mode: - filename = "index.html" - self.url = "" - else: - self.url = 'index%s.html' % mode - filename = self.url - self.outfile = os.path.join(outdir, filename) + def __init__(self, hostname, files, retcode): + super(FutureHost, self).__init__(hostname, files, retcode) + self.diff_file = files.file_for('future', 'diff') + + def _retcode_to_desc(self): + if self.retcode == 'break': + return 'change breaks the current parser' + elif self.retcode == 'error': + return 'change is not compatible with the future parser' + elif self.retcode == 'ok': + return 'change works with both parsers' + elif self.retcode == 'diff': + return 'change works with both parsers, with diffs' + + +class Index(object): + tpl = 'index.jinja2' + + def __init__(self, outdir): + filename = "index.html" + self.url = "" + self.outfile = os.path.join(outdir, self.page_name) def render(self, state): """ Render the index page with info coming from state """ _log.debug("Rendering the main index page") - t = env.get_template('index.jinja2') + t = env.get_template(self.tpl) # TODO: support multiple modes page = t.render(state, jid=job_id, chid=change_id) with open(self.outfile, 'w') as f: f.write(page) + + +class FutureIndex(Index): + tpl = 'index.future.jinja2' + page_name = 'index-future.html' diff --git a/puppet_compiler/puppet.py b/puppet_compiler/puppet.py index 7ab0f9e..dd07307 100644 --- a/puppet_compiler/puppet.py +++ b/puppet_compiler/puppet.py @@ -45,12 +45,12 @@ f.write(line) -def diff(env, hostname): +def diff(env, hostname, base='prod'): """ Compute the diffs between the two changes """ hostfiles = HostFiles(hostname) - prod_catalog = hostfiles.file_for('prod', 'catalog') + prod_catalog = hostfiles.file_for(base, 'catalog') change_catalog = hostfiles.file_for(env, 'catalog') output = hostfiles.file_for(env, 'diff') cmd = ['puppet', 'catalog', 'diff', '--show_resource_diff', diff --git a/puppet_compiler/state.py b/puppet_compiler/state.py index 51035da..bb076e5 100644 --- a/puppet_compiler/state.py +++ b/puppet_compiler/state.py @@ -47,3 +47,17 @@ return 'fail' else: return 'diff' + + +class FutureState(ChangeState): + + @property + def name(self): + if self.prod_error: + return 'break' + elif self.change_error or self.diff is False: + return 'error' + elif self.diff is None: + return 'ok' + else: + return 'diff' diff --git a/puppet_compiler/templates/hostpage.future.jinja2 b/puppet_compiler/templates/hostpage.future.jinja2 new file mode 100644 index 0000000..9956cc4 --- /dev/null +++ b/puppet_compiler/templates/hostpage.future.jinja2 @@ -0,0 +1,49 @@ +<!doctype html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>Parsers comparison for {{ host }}</title> + <style type="text/css"> + h1, h2, h3, h4 { + font-family: "HelveticaNeue-CondensedBold","Helvetica Neue","Arial Narrow",Arial,sans-serif; + } + .source { + font-family: Consolas, "Andale Mono", "Courier New", monospace + } + .err .fail { + color: red; + } + .noop { + color: darkgreen; + } + .diff { + color: darkgoldenrod; + } + </style> + </head> + <body> + <div id="main"> + {% block body %} + <h1>Compilation results for {{ host }}: <span class="{{retcode}}">{{ desc }}</span></h1> + {% if retcode == "diff" %} + <h2>Catalog differences</h2> + <textarea rows="20" cols="82" class="source">{{ diffs }}</textarea> + {% endif %} + <h2>Relevant files</h2> + <ul> + <li><a href="change.{{ host }}.pson">Old parser catalog</a></li> + <li><a href="change.{{ host }}-future.pson">Future parser catalog</a></li> + <li><a href="prod.{{ host }}-future.err">Old parser errors/warnings</a></li> + <li><a href="change.{{ host }}-future.err">Future parser errors/warnings</a></li> + </ul> + {% endblock %} + {% block extnav %} + <p> + <a href="https://integration.wikimedia.org/ci/job/operations-puppet-catalog-compiler/{{ jid }}/" target="_blank">Back to jenkins job</a> + <a href="https://gerrit.wikimedia.org/r/#/c/{{ chid }}/" target="_blank">See gerrit change</a> + </p> + {% endblock %} + </div> + </body> +</html> diff --git a/puppet_compiler/templates/index.future.jinja2 b/puppet_compiler/templates/index.future.jinja2 new file mode 100644 index 0000000..e5c7c39 --- /dev/null +++ b/puppet_compiler/templates/index.future.jinja2 @@ -0,0 +1,30 @@ +{% extends "hostpage.jinja2" %} +{% block body %} + <h1>Results for change <a href="https://gerrit.wikimedia.org/r/#/c/{{ chid }}/" target="_blank">{{ chid }} with the future parser.</a></h1> + <div id="list"> + <h2>Hosts that have no differences with the two parsers</h2> + <ul> + {% for host in state.ok|sort %} + <li> <a href="{{ host }}/">{{ host }}</a> + {% endfor %} + </ul> + <h2>Hosts that compile with differences</h2> + <ul> + {% for host in state.diff|sort %} + <li> <a href="{{ host }}/">{{ host }}</a> + {% endfor %} + </ul> + <h2>Hosts that fail to compile with the future parser</h2> + <ul> + {% for host in state.error|sort %} + <li> <a href="{{ host }}/">{{ host }}</a> + {% endfor %} + </ul> + <h2>Hosts that break with the current parser</h2> + <ul> + {% for host in state.break|sort %} + <li> <a href="{{ host }}/">{{ host }}</a> + {% endfor %} + </ul> + </div> +{% endblock %} diff --git a/puppet_compiler/worker.py b/puppet_compiler/worker.py index f345bcc..adb59b6 100644 --- a/puppet_compiler/worker.py +++ b/puppet_compiler/worker.py @@ -6,13 +6,13 @@ from puppet_compiler import puppet, _log from puppet_compiler.directories import HostFiles, FHS from puppet_compiler.presentation import html -from puppet_compiler.state import ChangeState +from puppet_compiler.state import ChangeState, FutureState class HostWorker(object): E_OK = 0 - E_PROD = 1 - E_CHANGE = 2 + E_BASE = 1 + E_CHANGED = 2 state = ChangeState html_page = html.Host html_index = html.Index @@ -44,8 +44,8 @@ diff = self._make_diff() else: diff = None - base = errors & self.E_PROD - change = errors & self.E_CHANGE + base = errors & self.E_BASE + change = errors & self.E_CHANGED try: self._make_output() state = self.state('', self.hostname, base, change, diff) @@ -55,42 +55,40 @@ exc_info=True) return (base, change, diff) + def _compile(self, env, args): + if env != 'prod' \ + and os.path.isfile(os.path.join(FHS.change_dir, 'src', '.configs')): + with open(os.path.join(FHS.change_dir, 'src', '.configs')) as f: + configs = f.readlines() + # Make sure every item has exactly 2 dashes prepended + configs = map(lambda x: "--{}".format(x.lstrip('-')), configs) + args.extend(configs) + + try: + _log.info("Compiling host %s (%s)", self.hostname, env) + puppet.compile(self.hostname, + env, + self.puppet_var, + *args) + except subprocess.CalledProcessError as e: + _log.error("Compilation failed for hostname %s " + " in environment %s.", self.hostname, env) + _log.info("Compilation exited with code %d", e.returncode) + _log.debug("Failed command: %s", e.cmd) + return False + else: + return True + def _compile_all(self): """ Does the grindwork of compiling the catalogs """ errors = self.E_OK - try: - _log.info("Compiling host %s (production)", self.hostname) - puppet.compile(self.hostname, - self._envs[0], - self.puppet_var) - except subprocess.CalledProcessError as e: - _log.error("Compilation failed for hostname %s " - " with the current tree.", self.hostname) - _log.info("Compilation exited with code %d", e.returncode) - _log.debug("Failed command: %s", e.cmd) - errors += self.E_PROD args = [] - if os.path.isfile(os.path.join(FHS.change_dir, 'src', '.configs')): - with open(os.path.join(FHS.change_dir, 'src', '.configs')) as f: - configs = f.readlines() - # Make sure every item has exactly 2 dashes prepended - configs = map(lambda x: "--{}".format(x.lstrip('-')), configs) - args.extend(configs) - try: - _log.info("Compiling host %s (change)", self.hostname) - puppet.compile(self.hostname, - self._envs[1], - self.puppet_var, - *args) - except subprocess.CalledProcessError as e: - _log.error("Compilation failed for hostname %s " - " with the change.", self.hostname) - _log.info("Compilation exited with code %d", e.returncode) - _log.debug("Failed command: %s", e.cmd) - errors += self.E_CHANGE - # Now test for regressions with the future parser + if not self._compile(self._envs[0], args): + errors += self.E_BASE + if not self._compile(self._envs[1], args): + errors += self.E_CHANGED return errors def _make_diff(self): @@ -157,3 +155,40 @@ # TODO: implement the actual html parsing host = self.html_page(self.hostname, self._files, retcode) host.htmlpage() + + +class FutureHostWorker(HostWorker): + """ + This worker is designed to be used when transitioning to the future parser. + It will compile the change first with the normal parser, then with the future one, + and make a diff between the two. + + Results: + "ok" => both catalogs compile, and there is no diff + "diff" => both catalogs compile, but there is a diff + "error" => normal parser works, but future parser doesn't + "break" => future parser works, but the normal one doesn't + """ + E_FUTURE = 4 + state_class = FutureState + html_page = html.FutureHost + html_index = html.FutureIndex + + def __init__(self, vardir, hostname): + super(FutureHostWorker, self).__init__(vardir, hostname) + self._envs = ['change', 'future'] + + def _compile_all(self): + future_args = [ + '--environment=future', + '--parser=future', + '--environmentpath=%s' % os.path.join(self.m.change_dir, 'src', environments), + '--default_manifest=\$confdir/manifests/site.pp' + ] + args = [] + errors = self.E_OK + if not self._compile(self._envs[0], args): + errors += self.E_BASE + if not self._compile(self._envs[1], future_args): + errors += self.E_CHANGED + return errors -- To view, visit https://gerrit.wikimedia.org/r/363808 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I5fc3eabd8704518dfeb17ebeab583010ca774690 Gerrit-PatchSet: 1 Gerrit-Project: operations/software/puppet-compiler Gerrit-Branch: master Gerrit-Owner: Giuseppe Lavagetto <glavage...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits