← Back to team overview

opencompute-developers team mailing list archive

[Merge] lp:~jeffmarcom/opencompute/add-checkbox-ocp-package into lp:opencompute/checkbox

 

Jeff Marcom has proposed merging lp:~jeffmarcom/opencompute/add-checkbox-ocp-package into lp:opencompute/checkbox.

Requested reviews:
  Open Compute Developers (opencompute-developers)

For more details, see:
https://code.launchpad.net/~jeffmarcom/opencompute/add-checkbox-ocp-package/+merge/181350

This adds a checkbox-ocp package to the list of built debian packages. This commit also bumps the overall changelog version to 1.16.6 so that it does not conflict with a previous checkbox installed package version.
-- 
https://code.launchpad.net/~jeffmarcom/opencompute/add-checkbox-ocp-package/+merge/181350
Your team Open Compute Developers is requested to review the proposed merge of lp:~jeffmarcom/opencompute/add-checkbox-ocp-package into lp:opencompute/checkbox.
=== added file 'bin/checkbox-ocp'
--- bin/checkbox-ocp	1970-01-01 00:00:00 +0000
+++ bin/checkbox-ocp	2013-08-21 16:29:49 +0000
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+export XDG_CACHE_HOME=${XDG_CACHE_HOME:-$HOME/.cache}
+export CHECKBOX_DATA=${CHECKBOX_DATA:-.}
+export CHECKBOX_SHARE=${CHECKBOX_SHARE:-.}
+export CHECKBOX_OPTIONS=${CHECKBOX_OPTIONS:---whitelist-file=$CHECKBOX_SHARE/data/whitelists/opencompute-ready-local.whitelist}
+export PYTHONPATH=$PYTHONPATH:$CHECKBOX_SHARE
+
+if [ $CHECKBOX_DATA != '.' ]
+then
+    old_data=$HOME/.checkbox
+    if [ -d $old_data ] && [ ! -d $CHECKBOX_DATA ]
+    then
+        mv -f $old_data $CHECKBOX_DATA
+    fi
+fi
+
+python3 $CHECKBOX_SHARE/run "$@" $CHECKBOX_SHARE/configs/$(basename $0).ini

=== added directory 'checkbox_ocp'
=== added file 'checkbox_ocp/__init__.py'
=== added file 'checkbox_ocp/cli_interface.py'
--- checkbox_ocp/cli_interface.py	1970-01-01 00:00:00 +0000
+++ checkbox_ocp/cli_interface.py	2013-08-21 16:29:49 +0000
@@ -0,0 +1,477 @@
+#
+# This file is part of Checkbox.
+#
+# Copyright 2008 Canonical Ltd.
+#
+# Checkbox 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.
+#
+# Checkbox 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 Checkbox.  If not, see <http://www.gnu.org/licenses/>.
+#
+import re
+import sys
+import string
+import termios
+
+from gettext import gettext as _
+
+from checkbox.user_interface import (UserInterface, ANSWER_TO_STATUS,
+    ALL_ANSWERS, YES_ANSWER, NO_ANSWER, SKIP_ANSWER)
+
+
+ANSWER_TO_OPTION = {
+    YES_ANSWER: _("yes"),
+    NO_ANSWER: _("no"),
+    SKIP_ANSWER: _("skip")}
+
+OPTION_TO_ANSWER = dict((o, a) for a, o in ANSWER_TO_OPTION.items())
+
+
+class CLIDialog:
+    """Command line dialog wrapper."""
+
+    def __init__(self, text):
+        self.text = text
+        self.visible = False
+
+    def put(self, text):
+        sys.stdout.write(text)
+        sys.stdout.flush()
+
+    def put_line(self, line):
+        self.put("%s\n" % line)
+
+    def put_newline(self):
+        self.put("\n")
+
+    def get(self, label=None, limit=1, separator=termios.CEOT):
+        if label is not None:
+            self.put_newline()
+            self.put(label)
+
+        fileno = sys.stdin.fileno()
+        saved_attributes = termios.tcgetattr(fileno)
+        attributes = termios.tcgetattr(fileno)
+        attributes[3] &= ~(termios.ICANON | termios.ECHO)
+        attributes[6][termios.VMIN] = 1
+        attributes[6][termios.VTIME] = 0
+        termios.tcsetattr(fileno, termios.TCSANOW, attributes)
+
+        input = []
+        escape = 0
+        try:
+            while len(input) < limit:
+                ch = sys.stdin.read(1)
+                if ord(ch) == separator:
+                    break
+                elif ord(ch) == 0o33:  # ESC
+                    escape = 1
+                elif ord(ch) == termios.CERASE or ord(ch) == 0o10:
+                    if len(input):
+                        self.put("\010 \010")
+                        del input[-1]
+                elif ord(ch) == termios.CKILL:
+                    self.put("\010 \010" * len(input))
+                    input = []
+                else:
+                    if not escape:
+                        input.append(ch)
+                        self.put(ch)
+                    elif escape == 1:
+                        if ch == "[":
+                            escape = 2
+                        else:
+                            escape = 0
+                    elif escape == 2:
+                        escape = 0
+        finally:
+            termios.tcsetattr(fileno, termios.TCSANOW, saved_attributes)
+
+        return "".join(input)
+
+    def show(self):
+        self.visible = True
+        self.put_newline()
+        self.put_line(self.text)
+
+
+class CLIChoiceDialog(CLIDialog):
+
+    def __init__(self, text):
+        super(CLIChoiceDialog, self).__init__(text)
+        self.keys = []
+        self.options = []
+
+    def get(self, *args, **kwargs):
+        response = super(CLIChoiceDialog, self).get(*args, **kwargs)
+        try:
+            return self.keys.index(response[0])
+        except ValueError:
+            return -1
+
+    def run(self, label=None, defaults=[]):
+        if not self.visible:
+            self.show()
+
+        try:
+            # Only one option
+            if len(self.keys) <= 1:
+                self.get(_("Press any key to continue..."))
+                return 0
+            # Multiple choices
+            while True:
+                self.put_newline()
+                for key, option in zip(self.keys, self.options):
+                    default = "*" if option in defaults else " "
+                    self.put_line("%s %s: %s" % (default, key, option))
+
+                response = self.get(_("Please choose (%s): ") %
+                                     ("/".join(self.keys)))
+                if response >= 0:
+                    return response
+
+                if label is not None:
+                    self.put_line(label)
+
+        except KeyboardInterrupt:
+            self.put_newline()
+            raise
+
+    def add_option(self, option, key=None):
+        if key is None:
+            keys = option.lower() + \
+                   string.ascii_letters + \
+                   string.digits + \
+                   string.punctuation
+
+            keys = keys.replace(' ', '')
+            keys = keys.replace('+', '')
+
+            for key in keys:
+                if key not in self.keys:
+                    break
+        self.keys.append(key)
+        self.options.append(option)
+
+
+class CLIReportDialog(CLIDialog):
+    """
+    Display test results
+    """
+    STATUS = {'pass': '{0}',
+              'fail': '{0}'}
+
+    def __init__(self, text, results):
+        super(CLIReportDialog, self).__init__(text)
+        self.results = results
+
+    def run(self):
+        """
+        Show root of the tree
+        and provide the ability to further display subtress
+        """
+        root = self.results
+        title = self.text
+        self._display(title, root)
+
+    def _is_suite(self, root):
+        """
+        Return True if root contains a suite
+        that is, a job containing other jobs
+        """
+        return all(issubclass(type(value), dict)
+                   for value in root.values())
+
+    def _display(self, title, root):
+        """
+        Display dialog until user decides to exit
+        (recursively for subtrees)
+        """
+        while True:
+            self.put_newline()
+            self.put_newline()
+            self.put_line(title)
+            self.put_newline()
+
+            keys = []
+            options = []
+
+            def add_option(option, key=None):
+                """
+                Add option to list
+                and generate automatic key value
+                if not provided
+                """
+                if key is None:
+                    key = string.ascii_lowercase[len(keys)]
+                keys.append(key)
+                options.append(option)
+
+            for job_name, job_data in sorted(root.items()):
+                if self._is_suite(job_data):
+                    add_option(job_name)
+                    self.put_line('{key}: {option}'
+                                  .format(key=keys[-1],
+                                          option=options[-1]))
+                else:
+                    job_status = job_data.get('status')
+                    status_string = (self.STATUS.get(job_status, '{0}')
+                                     .format(job_status))
+                    self.put_line('   {name} [{status}]'
+                                  .format(name=job_name,
+                                          status=status_string))
+
+            add_option(_("Space when finished"), " ")
+            self.put_line('{key}: {option}'
+                          .format(key=keys[-1],
+                                  option=options[-1]))
+
+            response = self.get(_("Please choose (%s): ") % ("/".join(keys)))
+
+            if response != ' ':
+                try:
+                    selected_option = options[keys.index(response)]
+                except ValueError:
+                    # Display again menu
+                    continue
+
+                # Display new menu with the contents of the selected option
+                self._display(selected_option, root[selected_option])
+            else:
+                # Exit from this menu display
+                # (display again parent menu or exit)
+                break
+
+
+class CLITextDialog(CLIDialog):
+
+    limit = 255
+    separator = termios.CEOT
+
+    def run(self, label=None):
+        if not self.visible:
+            self.show()
+
+        self.put_newline()
+        try:
+            return self.get(label, self.limit, self.separator)
+        except KeyboardInterrupt:
+            self.put_newline()
+            raise
+
+
+class CLILineDialog(CLITextDialog):
+
+    limit = 80
+    separator = ord("\n")
+
+
+class Twirly(object):
+    def __init__(self):
+        self.index = 0
+        self.twirlies = "-\\|/"
+
+    def next(self):
+        next_twirly = self.twirlies[self.index]
+        self.index = (self.index + 1) % len(self.twirlies)
+        return next_twirly
+
+
+class CLIProgressDialog(CLIDialog):
+    """Command line progress dialog wrapper."""
+
+    def __init__(self, text):
+        super(CLIProgressDialog, self).__init__(text)
+        self.progress_count = 0
+        self.twirly = Twirly()
+
+    def set(self, progress=None):
+        self.progress_count = (self.progress_count + 1) % 5
+        if self.progress_count:
+            return
+
+        if progress != None:
+            self.put("\r%u%%" % (progress * 100))
+        else:
+            self.put("\b\b")
+            self.put(self.twirly.next())
+            self.put(" ")
+        sys.stdout.flush()
+
+
+class CLIInterface(UserInterface):
+
+    def _toggle_results(self, key, options, results):
+        if isinstance(results, dict):
+            if key in results:
+                del results[key]
+
+            elif key in options:
+                if isinstance(options[key], dict):
+                    results[key] = {}
+                elif isinstance(options[key], (list, tuple)):
+                    results[key] = []
+                else:
+                    results[key] = None
+
+                for k in options[key]:
+                    self._toggle_results(k, options[key], results[key])
+
+        elif isinstance(results, (list, tuple)):
+            if key in results:
+                results.remove(key)
+            elif key in options:
+                results.append(key)
+
+    def show_progress_start(self, text):
+        self.progress = CLIProgressDialog(text)
+        self.progress.show()
+
+    def show_progress_pulse(self):
+        self.progress.set()
+
+    def show_text(self, text, previous=None, next=None):
+        dialog = CLIChoiceDialog(text)
+        dialog.run()
+
+    def show_entry(self, text, value, showSubmitToHexr=False, label=None, previous=None, next=None):
+        dialog = CLILineDialog(text)
+
+        return (dialog.run(), False)
+
+    def show_check(self, text, options=[], default=[]):
+        dialog = CLIChoiceDialog(text)
+        for option in options:
+            dialog.add_option(option)
+
+        dialog.add_option(_("Space when finished"), " ")
+
+        results = default
+        while True:
+            response = dialog.run(defaults=results)
+            if response >= len(options):
+                break
+
+            result = options[response]
+            self._toggle_results(result, options, results)
+
+        return (results, False)
+
+    def show_radio(self, text, options=[], default=None):
+        dialog = CLIChoiceDialog(text)
+        for option in options:
+            dialog.add_option(option)
+
+        # Show options dialog
+        response = dialog.run()
+        return options[response]
+
+    def show_tree(self, text, options={}, default={}, deselect_warning=""):
+        keys = sorted(options.keys())
+
+        dialog = CLIChoiceDialog(text)
+        for option in keys:
+            dialog.add_option(option)
+
+        dialog.add_option(_("Combine with character above to expand node"),
+                          "+")
+        dialog.add_option(_("Space when finished"), " ")
+
+        do_expand = False
+        results = default
+        while True:
+            response = dialog.run(defaults=results)
+            if response > len(options):
+                break
+
+            elif response == len(options):
+                response = dialog.get()
+                do_expand = True
+
+            else:
+                do_expand = False
+
+            # Invalid response
+            if response < 0:
+                continue
+
+            # Toggle results
+            result = keys[response]
+            if not do_expand:
+                self._toggle_results(result, options, results)
+                continue
+
+            # Expand tree
+            dialog.visible = False
+            if options[result]:
+                branch_results = results.get(result, {})
+                self.show_tree(result, options[result], branch_results)
+                if branch_results and result not in results:
+                    results[result] = branch_results
+
+        return results
+
+    def show_report(self, text, results):
+        """
+        Show test case results in a tree hierarchy
+        """
+        dialog = CLIReportDialog(text, results)
+        dialog.run()
+
+    def show_test(self, test, runner):
+        options = list([ANSWER_TO_OPTION[a] for a in ALL_ANSWERS])
+        if "command" in test:
+            options.append(_("test"))
+
+        if re.search(r"\$output", test["description"]):
+            output = runner(test)[1]
+        else:
+            output = ""
+
+        while True:
+            # Show option dialog
+            description = string.Template(test["description"]).substitute({
+                "output": output.strip()})
+            dialog = CLIChoiceDialog(description)
+
+            for option in options:
+                dialog.add_option(option, option[0])
+
+            # Get option from dialog
+            response = dialog.run()
+            option = options[response]
+            if response < len(ALL_ANSWERS):
+                break
+
+            output = runner(test)[1]
+
+            options[-1] = _("test again")
+
+        answer = OPTION_TO_ANSWER[option]
+        if answer == NO_ANSWER:
+            text = _("Further information:")
+            dialog = CLITextDialog(text)
+            test["data"] = dialog.run(_("Please type here and press"
+                " Ctrl-D when finished:\n"))
+        else:
+            test["data"] = ""
+
+        test["status"] = ANSWER_TO_STATUS[answer]
+
+    def show_info(self, text, options=[], default=None):
+        return self.show_radio(text, options, default)
+
+    def show_error(self, primary_text,
+                   secondary_text=None, detailed_text=None):
+        text = filter(None, [primary_text, secondary_text, detailed_text])
+        text = '\n'.join(text)
+        dialog = CLIChoiceDialog("Error: %s" % text)
+        dialog.run()

=== modified file 'debian/changelog'
--- debian/changelog	2013-08-19 20:46:26 +0000
+++ debian/changelog	2013-08-21 16:29:49 +0000
@@ -1,4 +1,12 @@
-checkbox (0.16.6~OCP) UNRELEASED; urgency=low
+checkbox (1.16.6~OCP) UNRELEASED; urgency=low
+
+  [ Jeff Marcom ]
+  * Added checkbox-ocp package
+
+ -- Jeff Marcom <jeff.marcom@xxxxxxxxxxxxx>  Wed, 21 Aug 2013 12:20:24 -0400
+
+
+checkbox (0.16.6~OCPubuntu1) UNRELEASED; urgency=low
 
   * INITIAL RELEASE:
     based on checkbox 0.16.6 from lp:checkbox with modificiations specific to
@@ -17,4 +25,3 @@
     checkbox/tests/ - Cleaned up unit tests that were looking for things
     removed in this merge.
 
- -- Jeff Marcom <jeff.marcom@xxxxxxxxxxxxx>  Wed, 31 Jul 2013 19:36:52 -0400

=== added file 'debian/checkbox-ocp.install'
--- debian/checkbox-ocp.install	1970-01-01 00:00:00 +0000
+++ debian/checkbox-ocp.install	2013-08-21 16:29:49 +0000
@@ -0,0 +1,3 @@
+usr/bin/checkbox-ocp
+usr/lib/python*/*-packages/checkbox_ocp/*
+usr/share/checkbox/examples/checkbox-ocp.ini

=== added file 'debian/checkbox-ocp.links'
--- debian/checkbox-ocp.links	1970-01-01 00:00:00 +0000
+++ debian/checkbox-ocp.links	2013-08-21 16:29:49 +0000
@@ -0,0 +1,1 @@
+usr/share/man/man1/checkbox.1.gz usr/share/man/man1/checkbox-ocp.1.gz

=== added file 'debian/checkbox-ocp.postinst'
--- debian/checkbox-ocp.postinst	1970-01-01 00:00:00 +0000
+++ debian/checkbox-ocp.postinst	2013-08-21 16:29:49 +0000
@@ -0,0 +1,7 @@
+#! /bin/sh -e
+
+base_package="checkbox"
+. /usr/share/debconf/confmodule
+. /usr/share/checkbox/install/postinst
+
+#DEBHELPER#

=== modified file 'debian/control'
--- debian/control	2013-07-05 16:43:48 +0000
+++ debian/control	2013-08-21 16:29:49 +0000
@@ -47,7 +47,7 @@
             python3-gi
 Suggests: bonnie++,
           bootchart,
-          checkbox-cli | checkbox-gtk,
+          checkbox-cli | checkbox-ocp, checkbox-gtk,
           curl,
           ethtool,
           fwts,
@@ -80,6 +80,15 @@
  .
  This package provides a command line interface for answering tests.
 
+Package: checkbox-ocp
+Architecture: all
+Depends: checkbox (>= ${source:Version}), ${misc:Depends}
+Description: Command line interface of checkbox for the Open Compute Project
+ This project provides an extensible interface for system testing on
+ Open Compute platforms.
+ .
+ This package provides a command line interface for answering tests.
+
 Package: checkbox-urwid
 Architecture: all
 Depends: checkbox (>= ${source:Version}), python3-urwid, ${misc:Depends}

=== modified file 'debian/rules'
--- debian/rules	2013-07-05 16:43:48 +0000
+++ debian/rules	2013-08-21 16:29:49 +0000
@@ -25,6 +25,7 @@
 
 override_dh_installdeb:
 	cp debian/checkbox.postrm debian/checkbox-cli.postrm
+	cp debian/checkbox.postrm debian/checkbox-ocp.postrm
 	cp debian/checkbox.postrm debian/checkbox-urwid.postrm
 	cp debian/checkbox.postrm debian/checkbox-gtk.postrm
 	cp debian/checkbox.postrm debian/checkbox-qt.postrm
@@ -34,6 +35,7 @@
 override_dh_installdocs:
 	dh_installdocs -pcheckbox ./README 
 	dh_installdocs -pcheckbox-cli ./README 
+	dh_installdocs -pcheckbox-ocp ./README 
 	dh_installdocs -pcheckbox-urwid ./README 
 	dh_installdocs -pcheckbox-gtk ./README 
 	dh_installdocs -pcheckbox-qt ./README
@@ -42,6 +44,6 @@
 
 override_dh_clean:
 	-find . -name \*.mo -exec rm {} \;
-	-rm -f debian/checkbox-cli.postrm debian/checkbox-urwid.postrm debian/checkbox-gtk.postrm debian/checkbox-qt.postrm debian/checkbox-hw-collection.postrm
+	-rm -f debian/checkbox-cli.postrm debian/checkbox-ocp.postrm debian/checkbox-cli.postrmebian/checkbox-urwid.postrm debian/checkbox-gtk.postrm debian/checkbox-qt.postrm debian/checkbox-hw-collection.postrm
 	debconf-updatepo
 	dh_clean

=== added file 'examples/checkbox-ocp.ini'
--- examples/checkbox-ocp.ini	1970-01-01 00:00:00 +0000
+++ examples/checkbox-ocp.ini	2013-08-21 16:29:49 +0000
@@ -0,0 +1,17 @@
+[DEFAULT]
+
+# Space separated list of files to include as a dependency for the
+# CLI interface.
+includes = %(checkbox_share)s/configs/checkbox.ini
+
+[checkbox/plugins/user_interface]
+
+# Module where the user interface implementation is defined.
+interface_module = checkbox_cli.cli_interface
+
+# Class implementing the UserInterface interface.
+interface_class = CLIInterface
+
+[checkbox/plugins]
+
+blacklist = hexr_prompt hexr_transport

=== modified file 'setup.py'
--- setup.py	2013-07-05 16:43:48 +0000
+++ setup.py	2013-08-21 16:29:49 +0000
@@ -266,8 +266,8 @@
         ],
     },
     scripts=[
-        "bin/checkbox-cli", "bin/checkbox-gtk", "bin/checkbox-urwid",
-        "bin/checkbox-qt", "bin/checkbox-hw-collection"],
+        "bin/checkbox-cli", "bin/checkbox-ocp", "bin/checkbox-gtk",
+        "bin/checkbox-urwid", "bin/checkbox-qt", "bin/checkbox-hw-collection"],
     packages=find_packages(),
     package_data={
         "": ["cputable"]},


Follow ups