Merge lp:~bladernr/opencompute/ocp-add-dcmitool-ipmitool-to-deps into lp:opencompute

Proposed by Jeff Lane 
Status: Superseded
Proposed branch: lp:~bladernr/opencompute/ocp-add-dcmitool-ipmitool-to-deps
Merge into: lp:opencompute
Diff against target: 609044 lines (+605588/-0) (has conflicts)
659 files modified
.bzr-builddeb/default.conf (+2/-0)
.bzrignore (+42/-0)
.checkbox-editor.cfg (+18/-0)
.gitignore (+26/-0)
.test-it.conf (+4/-0)
.travis.yml (+5/-0)
COPYING (+674/-0)
MANIFEST.in (+1/-0)
README (+5/-0)
README-PORT (+76/-0)
Vagrantfile (+56/-0)
apport/checkbox.py (+24/-0)
apport/source_checkbox.py (+20/-0)
backend (+66/-0)
bin/checkbox-hw-collection (+19/-0)
bin/checkbox-ocp-cli (+22/-0)
bin/checkbox-ocp-gtk (+22/-0)
bin/checkbox-ocp-qt (+22/-0)
bin/checkbox-ocp-urwid (+22/-0)
checkbox/application.py (+136/-0)
checkbox/arguments.py (+95/-0)
checkbox/attribute.py (+41/-0)
checkbox/component.py (+184/-0)
checkbox/contrib/REThread.py (+142/-0)
checkbox/contrib/bpickle.py (+188/-0)
checkbox/contrib/gdk.py (+46/-0)
checkbox/contrib/glock.py (+311/-0)
checkbox/contrib/persist.py (+552/-0)
checkbox/contrib/xrandr.py (+1064/-0)
checkbox/dbus/__init__.py (+89/-0)
checkbox/dbus/udisks2.py (+479/-0)
checkbox/dispatcher.py (+216/-0)
checkbox/heuristics/__init__.py (+56/-0)
checkbox/heuristics/tests/test_udisks2.py (+40/-0)
checkbox/heuristics/udev.py (+44/-0)
checkbox/heuristics/udisks2.py (+62/-0)
checkbox/job.py (+124/-0)
checkbox/lib/bit.py (+46/-0)
checkbox/lib/cache.py (+37/-0)
checkbox/lib/config.py (+170/-0)
checkbox/lib/conversion.py (+172/-0)
checkbox/lib/decorator.py (+28/-0)
checkbox/lib/dmi.py (+241/-0)
checkbox/lib/enum.py (+72/-0)
checkbox/lib/environ.py (+113/-0)
checkbox/lib/fifo.py (+107/-0)
checkbox/lib/input.py (+585/-0)
checkbox/lib/log.py (+77/-0)
checkbox/lib/path.py (+62/-0)
checkbox/lib/pci.py (+89/-0)
checkbox/lib/process.py (+140/-0)
checkbox/lib/redirect.py (+158/-0)
checkbox/lib/resolver.py (+145/-0)
checkbox/lib/safe.py (+100/-0)
checkbox/lib/script.py (+33/-0)
checkbox/lib/selector.py (+207/-0)
checkbox/lib/signal.py (+89/-0)
checkbox/lib/template.py (+143/-0)
checkbox/lib/template_i18n.py (+134/-0)
checkbox/lib/tests/test_resolver.py (+98/-0)
checkbox/lib/text.py (+100/-0)
checkbox/lib/transport.py (+306/-0)
checkbox/lib/tz.py (+55/-0)
checkbox/lib/update.py (+42/-0)
checkbox/lib/url.py (+41/-0)
checkbox/lib/usb.py (+59/-0)
checkbox/message.py (+334/-0)
checkbox/parsers/cpuinfo.py (+179/-0)
checkbox/parsers/cputable (+40/-0)
checkbox/parsers/cputable.py (+42/-0)
checkbox/parsers/deferred.py (+27/-0)
checkbox/parsers/description.py (+96/-0)
checkbox/parsers/device.py (+24/-0)
checkbox/parsers/dmidecode.py (+126/-0)
checkbox/parsers/efi.py (+52/-0)
checkbox/parsers/lshwjson.py (+23/-0)
checkbox/parsers/meminfo.py (+46/-0)
checkbox/parsers/modinfo.py (+89/-0)
checkbox/parsers/pactl.py (+543/-0)
checkbox/parsers/pyparsing.py (+3604/-0)
checkbox/parsers/submission.py (+568/-0)
checkbox/parsers/tests/fixtures/submission_attachment.xml (+6/-0)
checkbox/parsers/tests/fixtures/submission_info_cpuinfo.xml (+34/-0)
checkbox/parsers/tests/fixtures/submission_info_dmidecode.xml (+783/-0)
checkbox/parsers/tests/fixtures/submission_info_memory.xml (+48/-0)
checkbox/parsers/tests/fixtures/submission_info_udevadm.xml (+5365/-0)
checkbox/parsers/tests/fixtures/submission_lsbrelease.xml (+11/-0)
checkbox/parsers/tests/fixtures/submission_packages.xml (+13/-0)
checkbox/parsers/tests/fixtures/submission_processors.xml (+149/-0)
checkbox/parsers/tests/fixtures/submission_questions.xml (+17/-0)
checkbox/parsers/tests/fixtures/submission_udev.xml (+5364/-0)
checkbox/parsers/tests/fixtures/xinput_quantal.txt (+143/-0)
checkbox/parsers/tests/fixtures/xinput_toshiba.txt (+166/-0)
checkbox/parsers/tests/pactl_data/cards-desktop-precise-0.txt (+33/-0)
checkbox/parsers/tests/pactl_data/cards-desktop-precise-1.txt (+51/-0)
checkbox/parsers/tests/pactl_data/cards-desktop-precise-2.txt (+30/-0)
checkbox/parsers/tests/pactl_data/cards-desktop-precise.txt (+41/-0)
checkbox/parsers/tests/pactl_data/desktop-precise-radeon-hdmi-available.txt (+608/-0)
checkbox/parsers/tests/pactl_data/desktop-precise-radeon.txt (+608/-0)
checkbox/parsers/tests/pactl_data/desktop-precise-xps1340.txt (+466/-0)
checkbox/parsers/tests/pactl_data/desktop-precise.txt (+696/-0)
checkbox/parsers/tests/pactl_data/desktop-raring-t430s-dp-available.txt (+889/-0)
checkbox/parsers/tests/pactl_data/desktop-raring-t430s.txt (+889/-0)
checkbox/parsers/tests/pactl_data/modules-desktop-precise-0.txt (+8/-0)
checkbox/parsers/tests/pactl_data/modules-desktop-precise.txt (+213/-0)
checkbox/parsers/tests/pactl_data/samples-desktop-precise.txt (+60/-0)
checkbox/parsers/tests/pactl_data/sinks-desktop-precise-0.txt (+53/-0)
checkbox/parsers/tests/pactl_data/sinks-desktop-precise-1.txt (+56/-0)
checkbox/parsers/tests/pactl_data/sinks-desktop-precise.txt (+56/-0)
checkbox/parsers/tests/test_cputable.py (+74/-0)
checkbox/parsers/tests/test_description.py (+194/-0)
checkbox/parsers/tests/test_dmi.py (+80/-0)
checkbox/parsers/tests/test_dmidecode.py (+60/-0)
checkbox/parsers/tests/test_efi.py (+63/-0)
checkbox/parsers/tests/test_pactl.py (+561/-0)
checkbox/parsers/tests/test_submission.py (+183/-0)
checkbox/parsers/tests/test_udevadm.py (+80/-0)
checkbox/parsers/tests/test_xinput.py (+130/-0)
checkbox/parsers/udevadm.py (+538/-0)
checkbox/parsers/utils.py (+36/-0)
checkbox/parsers/xinput.py (+198/-0)
checkbox/plugin.py (+50/-0)
checkbox/properties.py (+209/-0)
checkbox/reactor.py (+118/-0)
checkbox/report.py (+170/-0)
checkbox/reports/launchpad_report.py (+287/-0)
checkbox/reports/xml_report.py (+193/-0)
checkbox/resource.py (+139/-0)
checkbox/scripts/audio_settings.py (+375/-0)
checkbox/scripts/gputest_benchmark.py (+106/-0)
checkbox/test_resource.py (+347/-0)
checkbox/tests/__init__.py (+49/-0)
checkbox/tests/test_job.py (+38/-0)
checkbox/tests/test_message_files.py (+123/-0)
checkbox/tests/test_report.py (+109/-0)
checkbox/tests/test_setup_files.py (+65/-0)
checkbox/udev.py (+93/-0)
checkbox/user_interface.py (+257/-0)
checkbox/variables.py (+303/-0)
checkbox_cli/cli_interface.py (+477/-0)
checkbox_gtk/gtk_interface.py (+647/-0)
checkbox_gtk/hyper_text_view.py (+114/-0)
checkbox_ocp/cli_interface.py (+477/-0)
checkbox_qt/qt_interface.py (+298/-0)
checkbox_urwid/urwid_interface.py (+1071/-0)
data/whitelists/default.whitelist (+143/-0)
data/whitelists/opencompute-ready-local.whitelist (+103/-0)
data/whitelists/opencompute-ready-remote.whitelist (+31/-0)
data/whitelists/server-cert.whitelist (+103/-0)
data/whitelists/sniff.whitelist (+35/-0)
debian/changelog (+45/-0)
debian/checkbox-hw-collection.install (+2/-0)
debian/checkbox-hw-collection.links (+1/-0)
debian/checkbox-hw-collection.postinst (+7/-0)
debian/checkbox-ocp-cli.install (+3/-0)
debian/checkbox-ocp-cli.links (+1/-0)
debian/checkbox-ocp-cli.postinst (+7/-0)
debian/checkbox-ocp-gtk.install (+4/-0)
debian/checkbox-ocp-gtk.links (+1/-0)
debian/checkbox-ocp-gtk.postinst (+7/-0)
debian/checkbox-ocp-qt.install (+6/-0)
debian/checkbox-ocp-qt.links (+1/-0)
debian/checkbox-ocp-qt.postinst (+7/-0)
debian/checkbox-ocp-urwid.install (+3/-0)
debian/checkbox-ocp-urwid.links (+1/-0)
debian/checkbox-ocp-urwid.postinst (+7/-0)
debian/checkbox.config (+27/-0)
debian/checkbox.dirs (+3/-0)
debian/checkbox.install (+19/-0)
debian/checkbox.links (+2/-0)
debian/checkbox.manpages (+1/-0)
debian/checkbox.postinst (+9/-0)
debian/checkbox.postrm (+26/-0)
debian/checkbox.templates (+83/-0)
debian/compat (+1/-0)
debian/control (+133/-0)
debian/copyright (+33/-0)
debian/po/POTFILES.in (+1/-0)
debian/po/ast.po (+210/-0)
debian/po/cs.po (+210/-0)
debian/po/de.po (+219/-0)
debian/po/en_AU.po (+210/-0)
debian/po/en_GB.po (+210/-0)
debian/po/es.po (+216/-0)
debian/po/fr.po (+212/-0)
debian/po/gl.po (+212/-0)
debian/po/he.po (+210/-0)
debian/po/hu.po (+210/-0)
debian/po/id.po (+218/-0)
debian/po/it.po (+216/-0)
debian/po/ja.po (+210/-0)
debian/po/nl.po (+218/-0)
debian/po/oc.po (+210/-0)
debian/po/pl.po (+210/-0)
debian/po/pt_BR.po (+214/-0)
debian/po/ro.po (+210/-0)
debian/po/ru.po (+210/-0)
debian/po/templates.pot (+210/-0)
debian/po/tr.po (+210/-0)
debian/po/uk.po (+210/-0)
debian/po/zh_CN.po (+210/-0)
debian/po/zh_TW.po (+210/-0)
debian/rules (+48/-0)
debian/source/format (+1/-0)
examples/checkbox-hw-collection.ini (+21/-0)
examples/checkbox-ocp-cli.ini (+17/-0)
examples/checkbox-ocp-gtk.ini (+16/-0)
examples/checkbox-ocp-qt.ini (+20/-0)
examples/checkbox-ocp-urwid.ini (+17/-0)
examples/checkbox.ini (+24/-0)
examples/network.cfg (+4/-0)
examples/org.freedesktop.policykit.checkbox.policy (+31/-0)
examples/virtualization.cfg (+4/-0)
gtk/checkbox-gtk.ui (+504/-0)
gtk/checkbox.svg (+2478/-0)
gtk/hyper_text_view.xml (+11/-0)
icons/scalable/apps/checkbox.svg (+336/-0)
install/config (+193/-0)
install/postinst (+72/-0)
jobs/benchmarks.txt.in (+297/-0)
jobs/cpu.txt.in (+50/-0)
jobs/dcmi_in_band.txt.in (+56/-0)
jobs/disk.txt.in (+120/-0)
jobs/info.txt.in (+226/-0)
jobs/ipmi.txt.in (+151/-0)
jobs/local.txt.in (+111/-0)
jobs/memory.txt.in (+28/-0)
jobs/miscellanea.txt.in (+148/-0)
jobs/networking.txt.in (+126/-0)
jobs/optical.txt.in (+165/-0)
jobs/power-management.txt.in (+232/-0)
jobs/resource.txt.in (+154/-0)
jobs/smoke.txt.in (+50/-0)
jobs/sniff.txt.in (+74/-0)
jobs/stress.txt.in (+239/-0)
jobs/usb.txt.in (+201/-0)
jobs/virtualization.txt.in (+40/-0)
lint (+146/-0)
man/checkbox.1 (+106/-0)
patches/0.1-ubuntu13 (+21/-0)
patches/0.1-ubuntu14 (+40/-0)
patches/0.1-ubuntu15 (+17/-0)
patches/0.1-ubuntu2 (+10/-0)
patches/0.1-ubuntu3 (+12/-0)
patches/0.1-ubuntu4 (+7/-0)
patches/0.1-ubuntu5 (+11/-0)
patches/0.1-ubuntu6 (+5/-0)
patches/0.1-ubuntu9 (+12/-0)
patches/0.14.2 (+18/-0)
patches/0.4 (+48/-0)
patches/0.5 (+30/-0)
patches/0.8 (+31/-0)
patches/0.9 (+39/-0)
plainbox/.coveragerc (+24/-0)
plainbox/.gitignore (+8/-0)
plainbox/.gitmodules (+3/-0)
plainbox/COPYING (+674/-0)
plainbox/MANIFEST.in (+10/-0)
plainbox/README.rst (+23/-0)
plainbox/Vagrantfile (+42/-0)
plainbox/contrib/com.canonical.certification.PlainBox1.service (+3/-0)
plainbox/contrib/dbus-mini-client.py (+430/-0)
plainbox/contrib/policykit_auth_admin_keep/org.freedesktop.policykit.pkexec.policy (+30/-0)
plainbox/contrib/policykit_yes/org.freedesktop.policykit.pkexec.policy (+29/-0)
plainbox/daily-package-testing/README (+18/-0)
plainbox/daily-package-testing/Vagrantfile (+48/-0)
plainbox/daily-package-testing/test-in-vagrant.sh (+59/-0)
plainbox/docs/appdev/index.rst (+39/-0)
plainbox/docs/author/checkbox-job-format.rst (+168/-0)
plainbox/docs/author/index.rst (+66/-0)
plainbox/docs/changelog.rst (+32/-0)
plainbox/docs/conf.py (+255/-0)
plainbox/docs/dev/architecture.rst (+40/-0)
plainbox/docs/dev/config.rst (+253/-0)
plainbox/docs/dev/index.rst (+19/-0)
plainbox/docs/dev/intro.rst (+244/-0)
plainbox/docs/dev/old.rst (+343/-0)
plainbox/docs/dev/reference.rst (+216/-0)
plainbox/docs/dev/resources.rst (+261/-0)
plainbox/docs/dev/trusted-launcher.rst (+209/-0)
plainbox/docs/glossary.rst (+104/-0)
plainbox/docs/index.rst (+65/-0)
plainbox/docs/usage.rst (+93/-0)
plainbox/mk-interesting-graphs.sh (+33/-0)
plainbox/mk-venv.sh (+195/-0)
plainbox/plainbox/__init__.py (+38/-0)
plainbox/plainbox/abc.py (+315/-0)
plainbox/plainbox/data/plainbox-ci-mailer.conf (+44/-0)
plainbox/plainbox/data/plainbox.conf (+26/-0)
plainbox/plainbox/data/plainbox.desktop (+10/-0)
plainbox/plainbox/data/report/checkbox.js (+16/-0)
plainbox/plainbox/data/report/hardware-1_0.rng (+533/-0)
plainbox/plainbox/data/report/styles.css (+258/-0)
plainbox/plainbox/impl/__init__.py (+123/-0)
plainbox/plainbox/impl/applogic.py (+287/-0)
plainbox/plainbox/impl/box.py (+100/-0)
plainbox/plainbox/impl/checkbox.py (+338/-0)
plainbox/plainbox/impl/color.py (+90/-0)
plainbox/plainbox/impl/commands/__init__.py (+372/-0)
plainbox/plainbox/impl/commands/analyze.py (+160/-0)
plainbox/plainbox/impl/commands/check_config.py (+92/-0)
plainbox/plainbox/impl/commands/checkbox.py (+103/-0)
plainbox/plainbox/impl/commands/crash.py (+74/-0)
plainbox/plainbox/impl/commands/dev.py (+64/-0)
plainbox/plainbox/impl/commands/logtest.py (+58/-0)
plainbox/plainbox/impl/commands/parse.py (+123/-0)
plainbox/plainbox/impl/commands/run.py (+415/-0)
plainbox/plainbox/impl/commands/script.py (+123/-0)
plainbox/plainbox/impl/commands/selftest.py (+86/-0)
plainbox/plainbox/impl/commands/service.py (+132/-0)
plainbox/plainbox/impl/commands/special.py (+159/-0)
plainbox/plainbox/impl/commands/sru.py (+291/-0)
plainbox/plainbox/impl/commands/test_dev.py (+78/-0)
plainbox/plainbox/impl/commands/test_parse.py (+133/-0)
plainbox/plainbox/impl/commands/test_run.py (+168/-0)
plainbox/plainbox/impl/commands/test_script.py (+158/-0)
plainbox/plainbox/impl/commands/test_sru.py (+76/-0)
plainbox/plainbox/impl/config.py (+558/-0)
plainbox/plainbox/impl/dbus/__init__.py (+47/-0)
plainbox/plainbox/impl/dbus/decorators.py (+351/-0)
plainbox/plainbox/impl/dbus/service.py (+662/-0)
plainbox/plainbox/impl/depmgr.py (+334/-0)
plainbox/plainbox/impl/exporter/__init__.py (+300/-0)
plainbox/plainbox/impl/exporter/html.py (+145/-0)
plainbox/plainbox/impl/exporter/json.py (+55/-0)
plainbox/plainbox/impl/exporter/rfc822.py (+48/-0)
plainbox/plainbox/impl/exporter/test_html.py (+142/-0)
plainbox/plainbox/impl/exporter/test_init.py (+222/-0)
plainbox/plainbox/impl/exporter/test_json.py (+63/-0)
plainbox/plainbox/impl/exporter/test_rfc822.py (+47/-0)
plainbox/plainbox/impl/exporter/test_text.py (+43/-0)
plainbox/plainbox/impl/exporter/test_xml.py (+96/-0)
plainbox/plainbox/impl/exporter/text.py (+41/-0)
plainbox/plainbox/impl/exporter/xlsx.py (+570/-0)
plainbox/plainbox/impl/exporter/xml.py (+387/-0)
plainbox/plainbox/impl/highlevel.py (+117/-0)
plainbox/plainbox/impl/integration_tests.py (+188/-0)
plainbox/plainbox/impl/job.py (+260/-0)
plainbox/plainbox/impl/logging.py (+420/-0)
plainbox/plainbox/impl/mock_job.py (+36/-0)
plainbox/plainbox/impl/parsers.py (+157/-0)
plainbox/plainbox/impl/plugins.py (+207/-0)
plainbox/plainbox/impl/provider.py (+165/-0)
plainbox/plainbox/impl/providers/__init__.py (+58/-0)
plainbox/plainbox/impl/providers/checkbox.py (+117/-0)
plainbox/plainbox/impl/providers/special.py (+69/-0)
plainbox/plainbox/impl/providers/stubbox/__init__.py (+43/-0)
plainbox/plainbox/impl/providers/stubbox/data/whitelists/stub.whitelist (+12/-0)
plainbox/plainbox/impl/providers/stubbox/data/whitelists/stub1.whitelist (+7/-0)
plainbox/plainbox/impl/providers/stubbox/data/whitelists/stub2.whitelist (+7/-0)
plainbox/plainbox/impl/providers/stubbox/jobs/local.txt.in (+5/-0)
plainbox/plainbox/impl/providers/stubbox/jobs/multilevel.txt.in (+20/-0)
plainbox/plainbox/impl/providers/stubbox/jobs/stub.txt.in (+80/-0)
plainbox/plainbox/impl/providers/stubbox/scripts/stub_package_list (+3/-0)
plainbox/plainbox/impl/providers/test_checkbox.py (+40/-0)
plainbox/plainbox/impl/providers/test_special.py (+104/-0)
plainbox/plainbox/impl/providers/v1.py (+229/-0)
plainbox/plainbox/impl/resource.py (+487/-0)
plainbox/plainbox/impl/result.py (+271/-0)
plainbox/plainbox/impl/rfc822.py (+287/-0)
plainbox/plainbox/impl/runner.py (+445/-0)
plainbox/plainbox/impl/secure/__init__.py (+27/-0)
plainbox/plainbox/impl/secure/checkbox_trusted_launcher.py (+407/-0)
plainbox/plainbox/impl/secure/test_checkbox_trusted_launcher.py (+280/-0)
plainbox/plainbox/impl/service.py (+1058/-0)
plainbox/plainbox/impl/session.py (+884/-0)
plainbox/plainbox/impl/session/__init__.py (+95/-0)
plainbox/plainbox/impl/session/jobs.py (+290/-0)
plainbox/plainbox/impl/session/legacy.py (+271/-0)
plainbox/plainbox/impl/session/manager.py (+241/-0)
plainbox/plainbox/impl/session/resume.py (+477/-0)
plainbox/plainbox/impl/session/state.py (+651/-0)
plainbox/plainbox/impl/session/storage.py (+608/-0)
plainbox/plainbox/impl/session/suspend.py (+321/-0)
plainbox/plainbox/impl/session/test_jobs.py (+218/-0)
plainbox/plainbox/impl/session/test_legacy.py (+65/-0)
plainbox/plainbox/impl/session/test_resume.py (+1290/-0)
plainbox/plainbox/impl/session/test_state.py (+556/-0)
plainbox/plainbox/impl/session/test_storage.py (+161/-0)
plainbox/plainbox/impl/session/test_suspend.py (+502/-0)
plainbox/plainbox/impl/signal.py (+128/-0)
plainbox/plainbox/impl/test_applogic.py (+170/-0)
plainbox/plainbox/impl/test_box.py (+299/-0)
plainbox/plainbox/impl/test_checkbox.py (+83/-0)
plainbox/plainbox/impl/test_color.py (+40/-0)
plainbox/plainbox/impl/test_config.py (+164/-0)
plainbox/plainbox/impl/test_depmgr.py (+313/-0)
plainbox/plainbox/impl/test_job.py (+349/-0)
plainbox/plainbox/impl/test_plugins.py (+133/-0)
plainbox/plainbox/impl/test_resource.py (+342/-0)
plainbox/plainbox/impl/test_result.py (+128/-0)
plainbox/plainbox/impl/test_rfc822.py (+329/-0)
plainbox/plainbox/impl/test_runner.py (+179/-0)
plainbox/plainbox/impl/test_session.py (+845/-0)
plainbox/plainbox/impl/test_signal.py (+46/-0)
plainbox/plainbox/impl/test_testing_utils.py (+54/-0)
plainbox/plainbox/impl/testing_utils.py (+121/-0)
plainbox/plainbox/impl/transport/__init__.py (+95/-0)
plainbox/plainbox/impl/transport/certification.py (+132/-0)
plainbox/plainbox/impl/transport/test_certification.py (+183/-0)
plainbox/plainbox/impl/transport/test_init.py (+68/-0)
plainbox/plainbox/public.py (+50/-0)
plainbox/plainbox/test-data/html-exporter/example-data.html (+10931/-0)
plainbox/plainbox/test-data/html-exporter/html-inliner.html (+19/-0)
plainbox/plainbox/test-data/integration-tests/cpu/scaling_test.json (+10/-0)
plainbox/plainbox/test-data/integration-tests/smoke/true.json (+11/-0)
plainbox/plainbox/test-data/xml-exporter/example-data.json (+19037/-0)
plainbox/plainbox/test-data/xml-exporter/example-data.xml (+15749/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_binary_attachment.json (+7/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_binary_attachment.xml (+21/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_comments.json (+13/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_comments.xml (+30/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_hardware_info.json (+10/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_hardware_info.xml (+22/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_io_log.json (+14/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_io_log.xml (+30/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_text_attachment.json (+7/-0)
plainbox/plainbox/test-data/xml-exporter/test_dump_with_text_attachment.xml (+21/-0)
plainbox/plainbox/test_abc.py (+36/-0)
plainbox/plainbox/test_public.py (+36/-0)
plainbox/plainbox/testing_utils/__init__.py (+72/-0)
plainbox/plainbox/testing_utils/cwd.py (+46/-0)
plainbox/plainbox/testing_utils/io.py (+108/-0)
plainbox/plainbox/testing_utils/resource.py (+104/-0)
plainbox/plainbox/testing_utils/test_cwd.py (+43/-0)
plainbox/plainbox/testing_utils/test_io.py (+78/-0)
plainbox/plainbox/testing_utils/test_testcases.py (+147/-0)
plainbox/plainbox/testing_utils/testcases.py (+297/-0)
plainbox/plainbox/tests.py (+56/-0)
plainbox/plainbox/vendor/__init__.py (+28/-0)
plainbox/plainbox/vendor/extcmd/__init__.py (+759/-0)
plainbox/plainbox/vendor/extcmd/test.py (+100/-0)
plainbox/plainbox/vendor/funcsigs/LICENSE (+13/-0)
plainbox/plainbox/vendor/funcsigs/__init__.py (+810/-0)
plainbox/plainbox/vendor/funcsigs/version.py (+1/-0)
plainbox/requirements/deb-core.txt (+2/-0)
plainbox/requirements/deb-dbus.txt (+1/-0)
plainbox/requirements/deb-docs.txt (+1/-0)
plainbox/requirements/pip-core.txt (+2/-0)
plainbox/requirements/pip-dbus.txt (+1/-0)
plainbox/requirements/pip-docs.txt (+1/-0)
plainbox/requirements/pip-optional.txt (+1/-0)
plainbox/requirements/pip-tests.txt (+1/-0)
plainbox/requirements/rtfd.txt (+5/-0)
plainbox/setup.cfg (+5/-0)
plainbox/setup.py (+72/-0)
plainbox/test-in-vagrant.sh (+82/-0)
plainbox/test-with-coverage.sh (+8/-0)
plugins/attachment_info.py (+39/-0)
plugins/backend_info.py (+192/-0)
plugins/begin_prompt.py (+33/-0)
plugins/client_info.py (+43/-0)
plugins/datetime_info.py (+36/-0)
plugins/delay_prompt.py (+37/-0)
plugins/environment_info.py (+62/-0)
plugins/error_prompt.py (+35/-0)
plugins/final_prompt.py (+42/-0)
plugins/gather_prompt.py (+39/-0)
plugins/hexr_prompt.py (+32/-0)
plugins/hexr_transport.py (+171/-0)
plugins/intro_prompt.py (+67/-0)
plugins/jobs_info.py (+332/-0)
plugins/jobs_prompt.py (+193/-0)
plugins/launchpad_exchange.py (+183/-0)
plugins/launchpad_prompt.py (+113/-0)
plugins/launchpad_report.py (+228/-0)
plugins/local_suite.py (+47/-0)
plugins/lock_prompt.py (+86/-0)
plugins/manual_test.py (+52/-0)
plugins/message_info.py (+142/-0)
plugins/metric_test.py (+41/-0)
plugins/persist_info.py (+70/-0)
plugins/proxy_info.py (+84/-0)
plugins/recover_prompt.py (+77/-0)
plugins/remote_suite.py (+55/-0)
plugins/report_prompt.py (+46/-0)
plugins/resource_info.py (+78/-0)
plugins/scripts_info.py (+41/-0)
plugins/server_info.py (+42/-0)
plugins/shell_test.py (+63/-0)
plugins/sleep_info.py (+42/-0)
plugins/submission_info.py (+64/-0)
plugins/subunit_report.py (+79/-0)
plugins/suites_prompt.py (+164/-0)
plugins/system_info.py (+89/-0)
plugins/tests_prompt.py (+33/-0)
plugins/user_interface.py (+116/-0)
plugins/warning_prompt.py (+35/-0)
po/Makevars (+42/-0)
po/POTFILES.in (+55/-0)
po/ace.po (+3782/-0)
po/af.po (+3776/-0)
po/am.po (+3803/-0)
po/ar.po (+4279/-0)
po/ast.po (+7372/-0)
po/az.po (+3767/-0)
po/be.po (+5465/-0)
po/bg.po (+4363/-0)
po/bn.po (+6311/-0)
po/bo.po (+3927/-0)
po/br.po (+4019/-0)
po/bs.po (+7518/-0)
po/ca.po (+4997/-0)
po/ca@valencia.po (+4766/-0)
po/checkbox.pot (+3754/-0)
po/ckb.po (+3839/-0)
po/cs.po (+7464/-0)
po/cy.po (+3770/-0)
po/da.po (+5678/-0)
po/de.po (+8280/-0)
po/dv.po (+3761/-0)
po/el.po (+7134/-0)
po/en_AU.po (+7417/-0)
po/en_CA.po (+4213/-0)
po/en_GB.po (+7648/-0)
po/eo.po (+4898/-0)
po/es.po (+7897/-0)
po/et.po (+3866/-0)
po/eu.po (+4011/-0)
po/fa.po (+3761/-0)
po/fi.po (+6510/-0)
po/fr.po (+7945/-0)
po/ga.po (+3761/-0)
po/gd.po (+5282/-0)
po/gl.po (+7547/-0)
po/he.po (+4654/-0)
po/hi.po (+4322/-0)
po/hr.po (+4089/-0)
po/hu.po (+7748/-0)
po/hy.po (+3761/-0)
po/id.po (+4114/-0)
po/is.po (+3877/-0)
po/it.po (+7894/-0)
po/ja.po (+5348/-0)
po/jbo.po (+3761/-0)
po/ka.po (+3764/-0)
po/kk.po (+3898/-0)
po/km.po (+5573/-0)
po/kn.po (+3788/-0)
po/ko.po (+4596/-0)
po/ku.po (+3788/-0)
po/ky.po (+3761/-0)
po/lt.po (+4083/-0)
po/lv.po (+5119/-0)
po/mk.po (+3798/-0)
po/ml.po (+3773/-0)
po/mr.po (+3779/-0)
po/ms.po (+7625/-0)
po/my.po (+3858/-0)
po/nb.po (+4107/-0)
po/nds.po (+3761/-0)
po/ne.po (+3846/-0)
po/nl.po (+7802/-0)
po/nn.po (+3789/-0)
po/oc.po (+5036/-0)
po/pl.po (+5151/-0)
po/ps.po (+3761/-0)
po/pt.po (+5273/-0)
po/pt_BR.po (+7815/-0)
po/ro.po (+6314/-0)
po/ru.po (+7908/-0)
po/sd.po (+3761/-0)
po/shn.po (+3761/-0)
po/si.po (+3788/-0)
po/sk.po (+4575/-0)
po/sl.po (+7529/-0)
po/sq.po (+5296/-0)
po/sr.po (+5410/-0)
po/sv.po (+5478/-0)
po/ta.po (+3781/-0)
po/te.po (+3770/-0)
po/th.po (+4064/-0)
po/tr.po (+6932/-0)
po/ug.po (+7341/-0)
po/uk.po (+4922/-0)
po/ur.po (+3761/-0)
po/uz.po (+3761/-0)
po/vi.po (+4313/-0)
po/zh_CN.po (+6090/-0)
po/zh_HK.po (+4266/-0)
po/zh_TW.po (+4566/-0)
qt/checkbox-qt-head.svg (+163/-0)
qt/checkbox-qt.desktop.in (+11/-0)
qt/com.canonical.QtCheckbox.service (+3/-0)
qt/frontend/checkboxtr.cpp (+49/-0)
qt/frontend/checkboxtr.h (+47/-0)
qt/frontend/frontend.pro (+29/-0)
qt/frontend/main.cpp (+18/-0)
qt/frontend/qtfront.cpp (+627/-0)
qt/frontend/qtfront.h (+113/-0)
qt/frontend/qtfront.ui (+1198/-0)
qt/frontend/resources.qrc (+5/-0)
qt/frontend/step.cpp (+62/-0)
qt/frontend/step.h (+13/-0)
qt/frontend/treemodel.cpp (+110/-0)
qt/frontend/treemodel.h (+22/-0)
report/checkbox.js (+16/-0)
report/checkbox.xsl (+188/-0)
report/hardware-1_0.rng (+533/-0)
report/styles.css (+258/-0)
requirements/deb-core.txt (+1/-0)
requirements/deb-jobs.txt (+1/-0)
requirements/pip-core.txt (+1/-0)
requirements/pip-tests.txt (+1/-0)
run (+33/-0)
scripts/ansi_parser (+160/-0)
scripts/block_device_resource (+71/-0)
scripts/c_and_I_Ready.py (+151/-0)
scripts/cdimage_resource (+100/-0)
scripts/clocktest.c (+124/-0)
scripts/cpu_offlining (+46/-0)
scripts/cpu_topology (+103/-0)
scripts/cpuinfo_resource (+56/-0)
scripts/disk_read_performance_test (+74/-0)
scripts/disk_smart (+247/-0)
scripts/disk_stats_test (+68/-0)
scripts/disk_stress (+51/-0)
scripts/dmi_resource (+55/-0)
scripts/dns_server_test (+49/-0)
scripts/dpkg_resource (+56/-0)
scripts/efi_resource (+54/-0)
scripts/filter_packages (+183/-0)
scripts/filter_templates (+134/-0)
scripts/frequency_governors_test (+648/-0)
scripts/fwts_test (+371/-0)
scripts/gconf_resource (+95/-0)
scripts/internet_test (+273/-0)
scripts/ipmi_test (+39/-0)
scripts/lsb_resource (+56/-0)
scripts/lsmod_info (+40/-0)
scripts/meminfo_resource (+46/-0)
scripts/memory_compare (+91/-0)
scripts/memory_test (+236/-0)
scripts/module_resource (+73/-0)
scripts/network (+388/-0)
scripts/network_check (+72/-0)
scripts/network_device_info (+263/-0)
scripts/network_info (+67/-0)
scripts/network_ntp_test (+212/-0)
scripts/ocp3_initialize.sh (+32/-0)
scripts/optical_detect (+29/-0)
scripts/optical_read_test (+134/-0)
scripts/optical_write_test (+143/-0)
scripts/removable_storage_test (+594/-0)
scripts/removable_storage_watcher (+890/-0)
scripts/run_templates (+144/-0)
scripts/spindown (+128/-0)
scripts/storage_test (+80/-0)
scripts/threaded_memtest.c (+421/-0)
scripts/udev_resource (+63/-0)
scripts/udisks2_monitor (+154/-0)
scripts/uname_resource (+39/-0)
scripts/virt_check (+43/-0)
scripts/virtualization (+284/-0)
setup.cfg (+27/-0)
setup.py (+282/-0)
tarmac-verify (+19/-0)
test (+217/-0)
test-in-vagrant.sh (+122/-0)
Conflict adding file .gitignore.  Moved existing file to .gitignore.moved.
Conflict adding file scripts.  Moved existing file to scripts.moved.
To merge this branch: bzr merge lp:~bladernr/opencompute/ocp-add-dcmitool-ipmitool-to-deps
Reviewer Review Type Date Requested Status
Open Compute Administrators Pending
Review via email: mp+188684@code.launchpad.net

This proposal has been superseded by a proposal from 2013-10-01.

Commit message

This adds dmcitool and ipmitool to recommends to make sure they get installed as well.

Description of the change

Don't know what happened to my other branch, but for some reason, LP just dumped all the code but kept the branch open :/ Maybe it was something screwed up on my end. IN any case, this is a re-try.

This adds dmcitool and ipmitool to recommends to make sure they get installed as well.

To post a comment you must log in.

Unmerged revisions

2164. By Jeff Lane 

Another test run highlighted that neither dmcitool nor ipmitool are installed either. I don't think it'll hurt to install them side by side, so this adds them to the recommends as well

2163. By Jeff Marcom

Trivial fix to debian/control for build-system parser

Signed-off-by: Jeff Marcom <email address hidden>

2162. By Jeff Marcom

Clean up job description for ipmi tests

Signed-off-by: Jeff Marcom <email address hidden>

2161. By Jeff Lane 

Promotes several test packages from suggests to recommends.

2160. By Jeff Lane 

This does two things:
1: Enables the storage_devices tests using bonnie++ after determining that Bonnie is easily available via both apt and yum (for CentOS).
2: Adds a new io_stress test for disks that uses the disk I/O capabilities of googles stressapptest

2159. By Jeff Marcom

Merge adds new ipmi inband tests and fixes the job file name and test names to be more appropriate. Also separates them from miscellanea as IPMI tests have grown to a full suite now.

2158. By Jeff Marcom

Updated idle/check expression

Signed-off-by: Jeff Marcom <email address hidden>

2157. By Jeff Marcom

Fix symlink check

Signed-off-by: Jeff Marcom <email address hidden>

2156. By Jeff Marcom

Quick patch to miscellanea/idle_check to include job description so unit test will pass

Signed-off-by: Jeff Marcom <email address hidden>

2155. By Jeff Marcom

Added a simple check to bypass the symlink creation in an Ubuntu based release.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory '.bzr-builddeb'
=== added file '.bzr-builddeb/default.conf'
--- .bzr-builddeb/default.conf 1970-01-01 00:00:00 +0000
+++ .bzr-builddeb/default.conf 2013-10-01 18:38:21 +0000
@@ -0,0 +1,2 @@
1[BUILDDEB]
2native = True
03
=== added file '.bzrignore'
--- .bzrignore 1970-01-01 00:00:00 +0000
+++ .bzrignore 2013-10-01 18:38:21 +0000
@@ -0,0 +1,42 @@
1__pycache__
2build
3checkbox.log
4checkbox.xsl
5debian/*-cli.postrm
6debian/*-gtk.postrm
7debian/*-qt.postrm
8debian/*-urwid.postrm
9debian/*.debhelper
10debian/*.log
11debian/*.substvars
12debian/checkbox
13debian/checkbox-cli
14debian/checkbox-gtk
15debian/checkbox-qt
16debian/checkbox-urwid
17debian/files
18debian/stamp-patched
19debian/tmp
20gtk/checkbox-gtk.glade.bak
21gtk/checkbox-gtk.gladep
22gtk/checkbox-gtk.gladep.bak
23lock
24plugins.bpickle
25plugins.bpickle.old
26python-build-stamp-2.5
27qt/frontend/Makefile
28qt/frontend/checkbox-qt-service
29qt/frontend/moc_qtfront.cpp
30qt/frontend/qrc_resources.cpp
31qt/frontend/ui_qtfront.h
32scripts/clocktest
33scripts/threaded_memtest
34store
35submission
36submission.xml
37submission.xml
38subunit.log
39system
40vagrant-logs/
41.vagrant
42plainbox/plainbox.egg-info
043
=== added file '.checkbox-editor.cfg'
--- .checkbox-editor.cfg 1970-01-01 00:00:00 +0000
+++ .checkbox-editor.cfg 2013-10-01 18:38:21 +0000
@@ -0,0 +1,18 @@
1[environment]
2checkbox_share = .
3
4[version control]
5push_branch = lp:checkbox
6pull_branch = lp:checkbox
7push_on_close = False
8commit_on_save = False
9pull_on_open = False
10exclude = .checkbox-editor.cfg
11
12[general]
13checkbox_data_cleanup = True
14replace_tabs_with_spaces = True
15number_of_spaces = 4
16checkbox_binary = ./bin/checkbox-gtk
17whitelist_resources = True
18
019
=== added file '.gitignore'
--- .gitignore 1970-01-01 00:00:00 +0000
+++ .gitignore 2013-10-01 18:38:21 +0000
@@ -0,0 +1,26 @@
1__pycache__
2# Ignore logs created by test-in-vagrant.sh
3vagrant-logs/
4# Ignore vagrant state file
5.vagrant
6# The list below is riddled with junk created by running the test suite of checkbox
7# It should be really cleaned up as tests should _not_ do that but right now it's more
8# annoying than dangerous so I'll get rid of the noise first
9*.o
10active_output
11checkbox.log
12checkbox.xsl
13plugins.bpickle
14plugins.bpickle.old
15qt/frontend/Makefile
16qt/frontend/checkbox-qt-service
17qt/frontend/moc_qtfront.cpp
18qt/frontend/qrc_resources.cpp
19qt/frontend/ui_qtfront.cpp
20qt/frontend/ui_qtfront.h
21store/
22submission.xml
23subunit.log
24build
25dist
26*.egg-info
027
=== renamed file '.gitignore' => '.gitignore.moved'
=== added file '.test-it.conf'
--- .test-it.conf 1970-01-01 00:00:00 +0000
+++ .test-it.conf 2013-10-01 18:38:21 +0000
@@ -0,0 +1,4 @@
1# Configuration data for test-it
2TI_RELEASES="lucid precise quantal raring"
3TI_CMD="python3 test"
4TI_DEPS="python3-lxml python3-setuptools"
05
=== added file '.travis.yml'
--- .travis.yml 1970-01-01 00:00:00 +0000
+++ .travis.yml 2013-10-01 18:38:21 +0000
@@ -0,0 +1,5 @@
1language: python
2python:
3 - "3.2"
4 - "3.3"
5script: cd plainbox && python3 setup.py test
06
=== added file 'COPYING'
--- COPYING 1970-01-01 00:00:00 +0000
+++ COPYING 2013-10-01 18:38:21 +0000
@@ -0,0 +1,674 @@
1 GNU GENERAL PUBLIC LICENSE
2 Version 3, 29 June 2007
3
4 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5 Everyone is permitted to copy and distribute verbatim copies
6 of this license document, but changing it is not allowed.
7
8 Preamble
9
10 The GNU General Public License is a free, copyleft license for
11software and other kinds of works.
12
13 The licenses for most software and other practical works are designed
14to take away your freedom to share and change the works. By contrast,
15the GNU General Public License is intended to guarantee your freedom to
16share and change all versions of a program--to make sure it remains free
17software for all its users. We, the Free Software Foundation, use the
18GNU General Public License for most of our software; it applies also to
19any other work released this way by its authors. You can apply it to
20your programs, too.
21
22 When we speak of free software, we are referring to freedom, not
23price. Our General Public Licenses are designed to make sure that you
24have the freedom to distribute copies of free software (and charge for
25them if you wish), that you receive source code or can get it if you
26want it, that you can change the software or use pieces of it in new
27free programs, and that you know you can do these things.
28
29 To protect your rights, we need to prevent others from denying you
30these rights or asking you to surrender the rights. Therefore, you have
31certain responsibilities if you distribute copies of the software, or if
32you modify it: responsibilities to respect the freedom of others.
33
34 For example, if you distribute copies of such a program, whether
35gratis or for a fee, you must pass on to the recipients the same
36freedoms that you received. You must make sure that they, too, receive
37or can get the source code. And you must show them these terms so they
38know their rights.
39
40 Developers that use the GNU GPL protect your rights with two steps:
41(1) assert copyright on the software, and (2) offer you this License
42giving you legal permission to copy, distribute and/or modify it.
43
44 For the developers' and authors' protection, the GPL clearly explains
45that there is no warranty for this free software. For both users' and
46authors' sake, the GPL requires that modified versions be marked as
47changed, so that their problems will not be attributed erroneously to
48authors of previous versions.
49
50 Some devices are designed to deny users access to install or run
51modified versions of the software inside them, although the manufacturer
52can do so. This is fundamentally incompatible with the aim of
53protecting users' freedom to change the software. The systematic
54pattern of such abuse occurs in the area of products for individuals to
55use, which is precisely where it is most unacceptable. Therefore, we
56have designed this version of the GPL to prohibit the practice for those
57products. If such problems arise substantially in other domains, we
58stand ready to extend this provision to those domains in future versions
59of the GPL, as needed to protect the freedom of users.
60
61 Finally, every program is threatened constantly by software patents.
62States should not allow patents to restrict development and use of
63software on general-purpose computers, but in those that do, we wish to
64avoid the special danger that patents applied to a free program could
65make it effectively proprietary. To prevent this, the GPL assures that
66patents cannot be used to render the program non-free.
67
68 The precise terms and conditions for copying, distribution and
69modification follow.
70
71 TERMS AND CONDITIONS
72
73 0. Definitions.
74
75 "This License" refers to version 3 of the GNU General Public License.
76
77 "Copyright" also means copyright-like laws that apply to other kinds of
78works, such as semiconductor masks.
79
80 "The Program" refers to any copyrightable work licensed under this
81License. Each licensee is addressed as "you". "Licensees" and
82"recipients" may be individuals or organizations.
83
84 To "modify" a work means to copy from or adapt all or part of the work
85in a fashion requiring copyright permission, other than the making of an
86exact copy. The resulting work is called a "modified version" of the
87earlier work or a work "based on" the earlier work.
88
89 A "covered work" means either the unmodified Program or a work based
90on the Program.
91
92 To "propagate" a work means to do anything with it that, without
93permission, would make you directly or secondarily liable for
94infringement under applicable copyright law, except executing it on a
95computer or modifying a private copy. Propagation includes copying,
96distribution (with or without modification), making available to the
97public, and in some countries other activities as well.
98
99 To "convey" a work means any kind of propagation that enables other
100parties to make or receive copies. Mere interaction with a user through
101a computer network, with no transfer of a copy, is not conveying.
102
103 An interactive user interface displays "Appropriate Legal Notices"
104to the extent that it includes a convenient and prominently visible
105feature that (1) displays an appropriate copyright notice, and (2)
106tells the user that there is no warranty for the work (except to the
107extent that warranties are provided), that licensees may convey the
108work under this License, and how to view a copy of this License. If
109the interface presents a list of user commands or options, such as a
110menu, a prominent item in the list meets this criterion.
111
112 1. Source Code.
113
114 The "source code" for a work means the preferred form of the work
115for making modifications to it. "Object code" means any non-source
116form of a work.
117
118 A "Standard Interface" means an interface that either is an official
119standard defined by a recognized standards body, or, in the case of
120interfaces specified for a particular programming language, one that
121is widely used among developers working in that language.
122
123 The "System Libraries" of an executable work include anything, other
124than the work as a whole, that (a) is included in the normal form of
125packaging a Major Component, but which is not part of that Major
126Component, and (b) serves only to enable use of the work with that
127Major Component, or to implement a Standard Interface for which an
128implementation is available to the public in source code form. A
129"Major Component", in this context, means a major essential component
130(kernel, window system, and so on) of the specific operating system
131(if any) on which the executable work runs, or a compiler used to
132produce the work, or an object code interpreter used to run it.
133
134 The "Corresponding Source" for a work in object code form means all
135the source code needed to generate, install, and (for an executable
136work) run the object code and to modify the work, including scripts to
137control those activities. However, it does not include the work's
138System Libraries, or general-purpose tools or generally available free
139programs which are used unmodified in performing those activities but
140which are not part of the work. For example, Corresponding Source
141includes interface definition files associated with source files for
142the work, and the source code for shared libraries and dynamically
143linked subprograms that the work is specifically designed to require,
144such as by intimate data communication or control flow between those
145subprograms and other parts of the work.
146
147 The Corresponding Source need not include anything that users
148can regenerate automatically from other parts of the Corresponding
149Source.
150
151 The Corresponding Source for a work in source code form is that
152same work.
153
154 2. Basic Permissions.
155
156 All rights granted under this License are granted for the term of
157copyright on the Program, and are irrevocable provided the stated
158conditions are met. This License explicitly affirms your unlimited
159permission to run the unmodified Program. The output from running a
160covered work is covered by this License only if the output, given its
161content, constitutes a covered work. This License acknowledges your
162rights of fair use or other equivalent, as provided by copyright law.
163
164 You may make, run and propagate covered works that you do not
165convey, without conditions so long as your license otherwise remains
166in force. You may convey covered works to others for the sole purpose
167of having them make modifications exclusively for you, or provide you
168with facilities for running those works, provided that you comply with
169the terms of this License in conveying all material for which you do
170not control copyright. Those thus making or running the covered works
171for you must do so exclusively on your behalf, under your direction
172and control, on terms that prohibit them from making any copies of
173your copyrighted material outside their relationship with you.
174
175 Conveying under any other circumstances is permitted solely under
176the conditions stated below. Sublicensing is not allowed; section 10
177makes it unnecessary.
178
179 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180
181 No covered work shall be deemed part of an effective technological
182measure under any applicable law fulfilling obligations under article
18311 of the WIPO copyright treaty adopted on 20 December 1996, or
184similar laws prohibiting or restricting circumvention of such
185measures.
186
187 When you convey a covered work, you waive any legal power to forbid
188circumvention of technological measures to the extent such circumvention
189is effected by exercising rights under this License with respect to
190the covered work, and you disclaim any intention to limit operation or
191modification of the work as a means of enforcing, against the work's
192users, your or third parties' legal rights to forbid circumvention of
193technological measures.
194
195 4. Conveying Verbatim Copies.
196
197 You may convey verbatim copies of the Program's source code as you
198receive it, in any medium, provided that you conspicuously and
199appropriately publish on each copy an appropriate copyright notice;
200keep intact all notices stating that this License and any
201non-permissive terms added in accord with section 7 apply to the code;
202keep intact all notices of the absence of any warranty; and give all
203recipients a copy of this License along with the Program.
204
205 You may charge any price or no price for each copy that you convey,
206and you may offer support or warranty protection for a fee.
207
208 5. Conveying Modified Source Versions.
209
210 You may convey a work based on the Program, or the modifications to
211produce it from the Program, in the form of source code under the
212terms of section 4, provided that you also meet all of these conditions:
213
214 a) The work must carry prominent notices stating that you modified
215 it, and giving a relevant date.
216
217 b) The work must carry prominent notices stating that it is
218 released under this License and any conditions added under section
219 7. This requirement modifies the requirement in section 4 to
220 "keep intact all notices".
221
222 c) You must license the entire work, as a whole, under this
223 License to anyone who comes into possession of a copy. This
224 License will therefore apply, along with any applicable section 7
225 additional terms, to the whole of the work, and all its parts,
226 regardless of how they are packaged. This License gives no
227 permission to license the work in any other way, but it does not
228 invalidate such permission if you have separately received it.
229
230 d) If the work has interactive user interfaces, each must display
231 Appropriate Legal Notices; however, if the Program has interactive
232 interfaces that do not display Appropriate Legal Notices, your
233 work need not make them do so.
234
235 A compilation of a covered work with other separate and independent
236works, which are not by their nature extensions of the covered work,
237and which are not combined with it such as to form a larger program,
238in or on a volume of a storage or distribution medium, is called an
239"aggregate" if the compilation and its resulting copyright are not
240used to limit the access or legal rights of the compilation's users
241beyond what the individual works permit. Inclusion of a covered work
242in an aggregate does not cause this License to apply to the other
243parts of the aggregate.
244
245 6. Conveying Non-Source Forms.
246
247 You may convey a covered work in object code form under the terms
248of sections 4 and 5, provided that you also convey the
249machine-readable Corresponding Source under the terms of this License,
250in one of these ways:
251
252 a) Convey the object code in, or embodied in, a physical product
253 (including a physical distribution medium), accompanied by the
254 Corresponding Source fixed on a durable physical medium
255 customarily used for software interchange.
256
257 b) Convey the object code in, or embodied in, a physical product
258 (including a physical distribution medium), accompanied by a
259 written offer, valid for at least three years and valid for as
260 long as you offer spare parts or customer support for that product
261 model, to give anyone who possesses the object code either (1) a
262 copy of the Corresponding Source for all the software in the
263 product that is covered by this License, on a durable physical
264 medium customarily used for software interchange, for a price no
265 more than your reasonable cost of physically performing this
266 conveying of source, or (2) access to copy the
267 Corresponding Source from a network server at no charge.
268
269 c) Convey individual copies of the object code with a copy of the
270 written offer to provide the Corresponding Source. This
271 alternative is allowed only occasionally and noncommercially, and
272 only if you received the object code with such an offer, in accord
273 with subsection 6b.
274
275 d) Convey the object code by offering access from a designated
276 place (gratis or for a charge), and offer equivalent access to the
277 Corresponding Source in the same way through the same place at no
278 further charge. You need not require recipients to copy the
279 Corresponding Source along with the object code. If the place to
280 copy the object code is a network server, the Corresponding Source
281 may be on a different server (operated by you or a third party)
282 that supports equivalent copying facilities, provided you maintain
283 clear directions next to the object code saying where to find the
284 Corresponding Source. Regardless of what server hosts the
285 Corresponding Source, you remain obligated to ensure that it is
286 available for as long as needed to satisfy these requirements.
287
288 e) Convey the object code using peer-to-peer transmission, provided
289 you inform other peers where the object code and Corresponding
290 Source of the work are being offered to the general public at no
291 charge under subsection 6d.
292
293 A separable portion of the object code, whose source code is excluded
294from the Corresponding Source as a System Library, need not be
295included in conveying the object code work.
296
297 A "User Product" is either (1) a "consumer product", which means any
298tangible personal property which is normally used for personal, family,
299or household purposes, or (2) anything designed or sold for incorporation
300into a dwelling. In determining whether a product is a consumer product,
301doubtful cases shall be resolved in favor of coverage. For a particular
302product received by a particular user, "normally used" refers to a
303typical or common use of that class of product, regardless of the status
304of the particular user or of the way in which the particular user
305actually uses, or expects or is expected to use, the product. A product
306is a consumer product regardless of whether the product has substantial
307commercial, industrial or non-consumer uses, unless such uses represent
308the only significant mode of use of the product.
309
310 "Installation Information" for a User Product means any methods,
311procedures, authorization keys, or other information required to install
312and execute modified versions of a covered work in that User Product from
313a modified version of its Corresponding Source. The information must
314suffice to ensure that the continued functioning of the modified object
315code is in no case prevented or interfered with solely because
316modification has been made.
317
318 If you convey an object code work under this section in, or with, or
319specifically for use in, a User Product, and the conveying occurs as
320part of a transaction in which the right of possession and use of the
321User Product is transferred to the recipient in perpetuity or for a
322fixed term (regardless of how the transaction is characterized), the
323Corresponding Source conveyed under this section must be accompanied
324by the Installation Information. But this requirement does not apply
325if neither you nor any third party retains the ability to install
326modified object code on the User Product (for example, the work has
327been installed in ROM).
328
329 The requirement to provide Installation Information does not include a
330requirement to continue to provide support service, warranty, or updates
331for a work that has been modified or installed by the recipient, or for
332the User Product in which it has been modified or installed. Access to a
333network may be denied when the modification itself materially and
334adversely affects the operation of the network or violates the rules and
335protocols for communication across the network.
336
337 Corresponding Source conveyed, and Installation Information provided,
338in accord with this section must be in a format that is publicly
339documented (and with an implementation available to the public in
340source code form), and must require no special password or key for
341unpacking, reading or copying.
342
343 7. Additional Terms.
344
345 "Additional permissions" are terms that supplement the terms of this
346License by making exceptions from one or more of its conditions.
347Additional permissions that are applicable to the entire Program shall
348be treated as though they were included in this License, to the extent
349that they are valid under applicable law. If additional permissions
350apply only to part of the Program, that part may be used separately
351under those permissions, but the entire Program remains governed by
352this License without regard to the additional permissions.
353
354 When you convey a copy of a covered work, you may at your option
355remove any additional permissions from that copy, or from any part of
356it. (Additional permissions may be written to require their own
357removal in certain cases when you modify the work.) You may place
358additional permissions on material, added by you to a covered work,
359for which you have or can give appropriate copyright permission.
360
361 Notwithstanding any other provision of this License, for material you
362add to a covered work, you may (if authorized by the copyright holders of
363that material) supplement the terms of this License with terms:
364
365 a) Disclaiming warranty or limiting liability differently from the
366 terms of sections 15 and 16 of this License; or
367
368 b) Requiring preservation of specified reasonable legal notices or
369 author attributions in that material or in the Appropriate Legal
370 Notices displayed by works containing it; or
371
372 c) Prohibiting misrepresentation of the origin of that material, or
373 requiring that modified versions of such material be marked in
374 reasonable ways as different from the original version; or
375
376 d) Limiting the use for publicity purposes of names of licensors or
377 authors of the material; or
378
379 e) Declining to grant rights under trademark law for use of some
380 trade names, trademarks, or service marks; or
381
382 f) Requiring indemnification of licensors and authors of that
383 material by anyone who conveys the material (or modified versions of
384 it) with contractual assumptions of liability to the recipient, for
385 any liability that these contractual assumptions directly impose on
386 those licensors and authors.
387
388 All other non-permissive additional terms are considered "further
389restrictions" within the meaning of section 10. If the Program as you
390received it, or any part of it, contains a notice stating that it is
391governed by this License along with a term that is a further
392restriction, you may remove that term. If a license document contains
393a further restriction but permits relicensing or conveying under this
394License, you may add to a covered work material governed by the terms
395of that license document, provided that the further restriction does
396not survive such relicensing or conveying.
397
398 If you add terms to a covered work in accord with this section, you
399must place, in the relevant source files, a statement of the
400additional terms that apply to those files, or a notice indicating
401where to find the applicable terms.
402
403 Additional terms, permissive or non-permissive, may be stated in the
404form of a separately written license, or stated as exceptions;
405the above requirements apply either way.
406
407 8. Termination.
408
409 You may not propagate or modify a covered work except as expressly
410provided under this License. Any attempt otherwise to propagate or
411modify it is void, and will automatically terminate your rights under
412this License (including any patent licenses granted under the third
413paragraph of section 11).
414
415 However, if you cease all violation of this License, then your
416license from a particular copyright holder is reinstated (a)
417provisionally, unless and until the copyright holder explicitly and
418finally terminates your license, and (b) permanently, if the copyright
419holder fails to notify you of the violation by some reasonable means
420prior to 60 days after the cessation.
421
422 Moreover, your license from a particular copyright holder is
423reinstated permanently if the copyright holder notifies you of the
424violation by some reasonable means, this is the first time you have
425received notice of violation of this License (for any work) from that
426copyright holder, and you cure the violation prior to 30 days after
427your receipt of the notice.
428
429 Termination of your rights under this section does not terminate the
430licenses of parties who have received copies or rights from you under
431this License. If your rights have been terminated and not permanently
432reinstated, you do not qualify to receive new licenses for the same
433material under section 10.
434
435 9. Acceptance Not Required for Having Copies.
436
437 You are not required to accept this License in order to receive or
438run a copy of the Program. Ancillary propagation of a covered work
439occurring solely as a consequence of using peer-to-peer transmission
440to receive a copy likewise does not require acceptance. However,
441nothing other than this License grants you permission to propagate or
442modify any covered work. These actions infringe copyright if you do
443not accept this License. Therefore, by modifying or propagating a
444covered work, you indicate your acceptance of this License to do so.
445
446 10. Automatic Licensing of Downstream Recipients.
447
448 Each time you convey a covered work, the recipient automatically
449receives a license from the original licensors, to run, modify and
450propagate that work, subject to this License. You are not responsible
451for enforcing compliance by third parties with this License.
452
453 An "entity transaction" is a transaction transferring control of an
454organization, or substantially all assets of one, or subdividing an
455organization, or merging organizations. If propagation of a covered
456work results from an entity transaction, each party to that
457transaction who receives a copy of the work also receives whatever
458licenses to the work the party's predecessor in interest had or could
459give under the previous paragraph, plus a right to possession of the
460Corresponding Source of the work from the predecessor in interest, if
461the predecessor has it or can get it with reasonable efforts.
462
463 You may not impose any further restrictions on the exercise of the
464rights granted or affirmed under this License. For example, you may
465not impose a license fee, royalty, or other charge for exercise of
466rights granted under this License, and you may not initiate litigation
467(including a cross-claim or counterclaim in a lawsuit) alleging that
468any patent claim is infringed by making, using, selling, offering for
469sale, or importing the Program or any portion of it.
470
471 11. Patents.
472
473 A "contributor" is a copyright holder who authorizes use under this
474License of the Program or a work on which the Program is based. The
475work thus licensed is called the contributor's "contributor version".
476
477 A contributor's "essential patent claims" are all patent claims
478owned or controlled by the contributor, whether already acquired or
479hereafter acquired, that would be infringed by some manner, permitted
480by this License, of making, using, or selling its contributor version,
481but do not include claims that would be infringed only as a
482consequence of further modification of the contributor version. For
483purposes of this definition, "control" includes the right to grant
484patent sublicenses in a manner consistent with the requirements of
485this License.
486
487 Each contributor grants you a non-exclusive, worldwide, royalty-free
488patent license under the contributor's essential patent claims, to
489make, use, sell, offer for sale, import and otherwise run, modify and
490propagate the contents of its contributor version.
491
492 In the following three paragraphs, a "patent license" is any express
493agreement or commitment, however denominated, not to enforce a patent
494(such as an express permission to practice a patent or covenant not to
495sue for patent infringement). To "grant" such a patent license to a
496party means to make such an agreement or commitment not to enforce a
497patent against the party.
498
499 If you convey a covered work, knowingly relying on a patent license,
500and the Corresponding Source of the work is not available for anyone
501to copy, free of charge and under the terms of this License, through a
502publicly available network server or other readily accessible means,
503then you must either (1) cause the Corresponding Source to be so
504available, or (2) arrange to deprive yourself of the benefit of the
505patent license for this particular work, or (3) arrange, in a manner
506consistent with the requirements of this License, to extend the patent
507license to downstream recipients. "Knowingly relying" means you have
508actual knowledge that, but for the patent license, your conveying the
509covered work in a country, or your recipient's use of the covered work
510in a country, would infringe one or more identifiable patents in that
511country that you have reason to believe are valid.
512
513 If, pursuant to or in connection with a single transaction or
514arrangement, you convey, or propagate by procuring conveyance of, a
515covered work, and grant a patent license to some of the parties
516receiving the covered work authorizing them to use, propagate, modify
517or convey a specific copy of the covered work, then the patent license
518you grant is automatically extended to all recipients of the covered
519work and works based on it.
520
521 A patent license is "discriminatory" if it does not include within
522the scope of its coverage, prohibits the exercise of, or is
523conditioned on the non-exercise of one or more of the rights that are
524specifically granted under this License. You may not convey a covered
525work if you are a party to an arrangement with a third party that is
526in the business of distributing software, under which you make payment
527to the third party based on the extent of your activity of conveying
528the work, and under which the third party grants, to any of the
529parties who would receive the covered work from you, a discriminatory
530patent license (a) in connection with copies of the covered work
531conveyed by you (or copies made from those copies), or (b) primarily
532for and in connection with specific products or compilations that
533contain the covered work, unless you entered into that arrangement,
534or that patent license was granted, prior to 28 March 2007.
535
536 Nothing in this License shall be construed as excluding or limiting
537any implied license or other defenses to infringement that may
538otherwise be available to you under applicable patent law.
539
540 12. No Surrender of Others' Freedom.
541
542 If conditions are imposed on you (whether by court order, agreement or
543otherwise) that contradict the conditions of this License, they do not
544excuse you from the conditions of this License. If you cannot convey a
545covered work so as to satisfy simultaneously your obligations under this
546License and any other pertinent obligations, then as a consequence you may
547not convey it at all. For example, if you agree to terms that obligate you
548to collect a royalty for further conveying from those to whom you convey
549the Program, the only way you could satisfy both those terms and this
550License would be to refrain entirely from conveying the Program.
551
552 13. Use with the GNU Affero General Public License.
553
554 Notwithstanding any other provision of this License, you have
555permission to link or combine any covered work with a work licensed
556under version 3 of the GNU Affero General Public License into a single
557combined work, and to convey the resulting work. The terms of this
558License will continue to apply to the part which is the covered work,
559but the special requirements of the GNU Affero General Public License,
560section 13, concerning interaction through a network will apply to the
561combination as such.
562
563 14. Revised Versions of this License.
564
565 The Free Software Foundation may publish revised and/or new versions of
566the GNU General Public License from time to time. Such new versions will
567be similar in spirit to the present version, but may differ in detail to
568address new problems or concerns.
569
570 Each version is given a distinguishing version number. If the
571Program specifies that a certain numbered version of the GNU General
572Public License "or any later version" applies to it, you have the
573option of following the terms and conditions either of that numbered
574version or of any later version published by the Free Software
575Foundation. If the Program does not specify a version number of the
576GNU General Public License, you may choose any version ever published
577by the Free Software Foundation.
578
579 If the Program specifies that a proxy can decide which future
580versions of the GNU General Public License can be used, that proxy's
581public statement of acceptance of a version permanently authorizes you
582to choose that version for the Program.
583
584 Later license versions may give you additional or different
585permissions. However, no additional obligations are imposed on any
586author or copyright holder as a result of your choosing to follow a
587later version.
588
589 15. Disclaimer of Warranty.
590
591 THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599
600 16. Limitation of Liability.
601
602 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610SUCH DAMAGES.
611
612 17. Interpretation of Sections 15 and 16.
613
614 If the disclaimer of warranty and limitation of liability provided
615above cannot be given local legal effect according to their terms,
616reviewing courts shall apply local law that most closely approximates
617an absolute waiver of all civil liability in connection with the
618Program, unless a warranty or assumption of liability accompanies a
619copy of the Program in return for a fee.
620
621 END OF TERMS AND CONDITIONS
622
623 How to Apply These Terms to Your New Programs
624
625 If you develop a new program, and you want it to be of the greatest
626possible use to the public, the best way to achieve this is to make it
627free software which everyone can redistribute and change under these terms.
628
629 To do so, attach the following notices to the program. It is safest
630to attach them to the start of each source file to most effectively
631state the exclusion of warranty; and each file should have at least
632the "copyright" line and a pointer to where the full notice is found.
633
634 <one line to give the program's name and a brief idea of what it does.>
635 Copyright (C) <year> <name of author>
636
637 This program is free software: you can redistribute it and/or modify
638 it under the terms of the GNU General Public License as published by
639 the Free Software Foundation, either version 3 of the License, or
640 (at your option) any later version.
641
642 This program is distributed in the hope that it will be useful,
643 but WITHOUT ANY WARRANTY; without even the implied warranty of
644 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 GNU General Public License for more details.
646
647 You should have received a copy of the GNU General Public License
648 along with this program. If not, see <http://www.gnu.org/licenses/>.
649
650Also add information on how to contact you by electronic and paper mail.
651
652 If the program does terminal interaction, make it output a short
653notice like this when it starts in an interactive mode:
654
655 <program> Copyright (C) <year> <name of author>
656 This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 This is free software, and you are welcome to redistribute it
658 under certain conditions; type `show c' for details.
659
660The hypothetical commands `show w' and `show c' should show the appropriate
661parts of the General Public License. Of course, your program's commands
662might be different; for a GUI interface, you would use an "about box".
663
664 You should also get your employer (if you work as a programmer) or school,
665if any, to sign a "copyright disclaimer" for the program, if necessary.
666For more information on this, and how to apply and follow the GNU GPL, see
667<http://www.gnu.org/licenses/>.
668
669 The GNU General Public License does not permit incorporating your program
670into proprietary programs. If your program is a subroutine library, you
671may consider it more useful to permit linking proprietary applications with
672the library. If this is what you want to do, use the GNU Lesser General
673Public License instead of this License. But first, please read
674<http://www.gnu.org/philosophy/why-not-lgpl.html>.
0675
=== added file 'MANIFEST.in'
--- MANIFEST.in 1970-01-01 00:00:00 +0000
+++ MANIFEST.in 2013-10-01 18:38:21 +0000
@@ -0,0 +1,1 @@
1include checkbox/parsers/tests/pactl_data/*.txt
02
=== added file 'README'
--- README 1970-01-01 00:00:00 +0000
+++ README 2013-10-01 18:38:21 +0000
@@ -0,0 +1,5 @@
1This is Checkbox which provides an extensible interface for system testing.
2
3See the following page for documentation:
4
5https://wiki.ubuntu.com/Testing/Automation/Checkbox
06
=== added file 'README-PORT'
--- README-PORT 1970-01-01 00:00:00 +0000
+++ README-PORT 2013-10-01 18:38:21 +0000
@@ -0,0 +1,76 @@
1The following steps will port your Checkbox source from Ubuntu to CentOS 6.4.
2
3Note: Following steps are on an Ubuntu system.
41) Install the devscripts package to create a debian package from source.
5 # apt-get install devscripts
6
72) Perform the following command in the Checkbox source code root directory to create
8 a debian package.
9 /checkbox# debuild -i -us -uc -b
10
113) Move up to the parent directory to view the debian packages that were built:
12 # cd ..
13
144) Download alien to convert the .deb packages into .rpm packages.
15 # apt-get install alien
16
175) Convert the .deb packages into .rpm packages using the following command:
18 alien -r package_name.deb --scripts
19
206) Transfer the checkbox*.rpm files to the CentOS 6.4 machine.
21
22Note: Following steps are on a CentOS 6.4 system.
237) Install python3 on the CentOS machnine.
24 7a) Install Development tools to be able to build from source
25 # yum groupinstall "Development tools"
26
27 7b) Install extra libraries for the python3 interpreter
28 # yum install zlib-devel bzip2-devel openssl-devel ncurses-devel sqllite-devel readline-devel tk-devel
29
30 7c) Add the Princeton University and the Institute for Advanced Study (PUIAS) repository to your mirrorlist to install python3.
31 i. Download the rpm gpg key
32 # cd /etc/pki/rpm-gpg
33 # wget -q http://springdale.math.ias.edu/data/puias/6/x86_64/os/RPM-GPG-KEY-puias
34 # rpm --import RPM-GPG-KEY-puias
35 ii. Create the repository file as /etc/yum.repos.d/puias-computational.repo
36 # cd /etc/yum.repos.d
37 # touch puias-computational.repo
38 # editor(vim, nano, etc) puias-computational.repo
39 [PUIAS_6_computational]
40 name=PUIAS computational BASE $releasever - $basearch
41 mirrorlist=http://puias.math.ias.edu/data/puias/computational/$releasever/$basearch/mirrorlist
42 #baseurl=http://puias.math.ias.edu/data/puias/computational/$releasever/$basearch
43 gpgcheck=1
44 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-puias
45
46 7d) Search for python3 in your repo.
47 # yum search python3
48
49 7e) Install python3 if it is returned after the search.
50 # yum install python3
51
528) Install Distribute to allow python modules to be installed
53 8a) Download Distribute source code.
54 # wget http://pypi.python.org/packages/source/d/distribute/distribute-0.6.35.tar.gz
55
56 8b) Extract and Install
57 # tar -xzvf distribute-0.6.35.tar.gz
58 # cd distribute-0.6.35
59 # python3 setup.py build && python3 setup.py install
60
619) Install the lxml python module that is needed by checkbox
62 9a) Install lxml dependencies
63 # yum install libxml2-devel libxslt-devel
64
65 9b) Install module
66 # easy_install-3.3 lxml
67
6810) Install the checkbox*.rpm files in the directory they were stored in.
69 # rpm -Uvh checkbox*.rpm
70
7111) Copy the checkbox files from /usr/lib/python3/dist-packages into /usr/lib/python3.3/site-packages/ or else checkbox won't run.
72 # cp -r /usr/lib/python3/dist-packages/checkbox* /usr/lib/python3.3/site-packages/
73
7412) Run checkbox at the terminal with the following command
75 # checkbox-ocp
76
077
=== added file 'Vagrantfile'
--- Vagrantfile 1970-01-01 00:00:00 +0000
+++ Vagrantfile 2013-10-01 18:38:21 +0000
@@ -0,0 +1,56 @@
1# -*- mode: ruby -*-
2# vi: set ft=ruby sw=2 ts=2 :
3
4Vagrant::Config.run do |config|
5
6 config.ssh.timeout = 60
7
8 # Define a Ubuntu Server image (cloud) for the 12.04 release (precise)
9 config.vm.define :precise do |precise_config|
10 precise_config.vm.box = "precise-cloud-i386"
11 precise_config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/precise/current/precise-server-cloudimg-i386-vagrant-disk1.box"
12 end
13
14 # Define a Ubuntu Server image (cloud) for the 12.10 release (quantal)
15 config.vm.define :quantal do |quantal_config|
16 quantal_config.vm.box = "quantal-cloud-i386"
17 quantal_config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/quantal/current/quantal-server-cloudimg-i386-vagrant-disk1.box"
18 end
19
20 # Define a Ubuntu Server image (cloud) for the 13.04 release (raring)
21 config.vm.define :raring do |raring_config|
22 raring_config.vm.box = "raring-cloud-i386"
23 raring_config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/raring/current/raring-server-cloudimg-i386-vagrant-disk1.box"
24 end
25
26 # For debugging and later future GUI testing
27 if ENV.key? "VAGRANT_GUI"
28 config.vm.boot_mode = :gui
29 end
30
31 # Setup an apt cache if one is available
32 if ENV.key? "VAGRANT_APT_CACHE"
33 config.vm.provision :shell, :inline => "echo 'Acquire::http { Proxy \"#{ENV['VAGRANT_APT_CACHE']}\"; };' > /etc/apt/apt.conf"
34 end
35
36 # Update to have the latest packages, this is needed because the image comes
37 # with an old (and no longer working) apt cache and links to many packages no
38 # longer work.
39 config.vm.provision :shell, :inline => "apt-get update && DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade --yes"
40 # Install dependencies from native packages
41 config.vm.provision :shell, :inline => "apt-get install --yes python3-setuptools python3-pkg-resources python3-lxml"
42 # Install python3-mock so that we can create mock objects for testing
43 config.vm.provision :shell, :inline => "apt-get install --yes python3-mock"
44 # Install python3-sphinx so that we can build documentation
45 config.vm.provision :shell, :inline => "apt-get install --yes python3-sphinx"
46 # Install policykit-1 so that we have pkexec
47 config.vm.provision :shell, :inline => "apt-get install --yes policykit-1"
48 # Install some checkbox script dependencies:
49 # Later on those could be installed on demand to test how we behave without
50 # them but for now that's good enough. Little by little...
51 config.vm.provision :shell, :inline => "apt-get install --yes fwts"
52 # Develop plainbox so that we have it in $PATH
53 config.vm.provision :shell, :inline => "cd /vagrant/plainbox/ && python3 setup.py develop"
54 # Create a cool symlink so that everyone knows where to go to
55 config.vm.provision :shell, :inline => "ln --no-dereference --force --symbolic /vagrant /home/vagrant/checkbox"
56end
057
=== added directory 'apport'
=== added file 'apport/checkbox.py'
--- apport/checkbox.py 1970-01-01 00:00:00 +0000
+++ apport/checkbox.py 2013-10-01 18:38:21 +0000
@@ -0,0 +1,24 @@
1'''apport general hook for checkbox
2
3(c) 2009 Canonical Ltd.
4Author: David Murphy <schwuk@ubuntu.com>
5'''
6import os
7
8
9def add_info(report):
10 HOME = os.environ.get('HOME', '/')
11 CACHE_HOME = os.environ.get('XDG_CACHE_HOME',
12 os.path.join(HOME, '.cache'))
13 SYSTEM = os.path.join(CACHE_HOME, 'checkbox', 'system')
14 SUBMISSION = os.path.join(CACHE_HOME, 'checkbox', 'submission')
15
16 try:
17 report['CheckboxSystem'] = open(SYSTEM).read()
18 except IOError:
19 pass
20
21 try:
22 report['CheckboxSubmission'] = open(SUBMISSION).read()
23 except IOError:
24 pass
025
=== added file 'apport/source_checkbox.py'
--- apport/source_checkbox.py 1970-01-01 00:00:00 +0000
+++ apport/source_checkbox.py 2013-10-01 18:38:21 +0000
@@ -0,0 +1,20 @@
1'''apport package hook for checkbox
2
3(c) 2009 Canonical Ltd.
4Author: David Murphy <schwuk@ubuntu.com>
5'''
6import os
7
8from apport.hookutils import attach_file_if_exists
9
10
11def add_info(report):
12 HOME = os.environ.get('HOME', '/')
13 CACHE_HOME = os.environ.get('XDG_CACHE_HOME',
14 os.path.join(HOME, '.cache'))
15
16 SUBMISSION = os.path.join(CACHE_HOME, 'checkbox', 'submission.xml')
17 attach_file_if_exists(report, SUBMISSION)
18
19 CHECKBOX_LOG = os.path.join(CACHE_HOME, 'checkbox', 'checkbox.log')
20 attach_file_if_exists(report, CHECKBOX_LOG)
021
=== added file 'backend'
--- backend 1970-01-01 00:00:00 +0000
+++ backend 2013-10-01 18:38:21 +0000
@@ -0,0 +1,66 @@
1#!/usr/bin/python3
2
3import os
4import sys
5
6from optparse import OptionParser
7from checkbox.lib.fifo import FifoReader, FifoWriter
8
9from checkbox.job import Job, FAIL
10
11
12def main(args):
13 usage = "Usage: %prog INPUT OUTPUT"
14 parser = OptionParser(usage=usage)
15 parser.add_option("-p", "--path",
16 help="PATH variable to replace in the environment")
17 (options, args) = parser.parse_args(args)
18
19 if len(args) < 2:
20 parser.error("Missing INPUT and OUTPUT")
21
22 # Set PATH
23 if options.path:
24 os.environ["PATH"] = options.path
25
26 # Set PYTHONPATH
27 pythonpath = sys.path[0]
28 if "PYTHONPATH" in os.environ:
29 pythonpath = ":".join([pythonpath, os.environ["PYTHONPATH"]])
30 os.environ["PYTHONPATH"] = pythonpath
31
32 reader = FifoReader(args[0])
33 writer = FifoWriter(args[1])
34
35 while True:
36 try:
37 message = reader.read_object()
38 #"unpack" the message
39 sequence, message = message
40 if message == "stop":
41 break
42 if message == "ping":
43 #Build a tuple with the sequence number as
44 #received
45 writer.write_object((sequence, "pong",))
46 continue
47 if isinstance(message, dict) and "command" in message:
48 job = Job(message["command"], message.get("environ"),
49 message.get("timeout"))
50 status, data, duration = job.execute()
51 try:
52 data = data.decode("utf-8")
53 except UnicodeDecodeError:
54 status, data, duration = (FAIL, "Decode error", 0,)
55 else:
56 status, data, duration = (FAIL, "", 0,)
57 #Build a tuple with sequence number
58 writer.write_object((sequence, (status, data, duration,),))
59 except IOError:
60 break
61
62 return 0
63
64
65if __name__ == "__main__":
66 sys.exit(main(sys.argv[1:]))
067
=== added directory 'bin'
=== added file 'bin/checkbox-hw-collection'
--- bin/checkbox-hw-collection 1970-01-01 00:00:00 +0000
+++ bin/checkbox-hw-collection 2013-10-01 18:38:21 +0000
@@ -0,0 +1,19 @@
1#!/bin/bash
2
3set -e
4
5export CHECKBOX_DATA=${CHECKBOX_DATA:-~/.checkbox-hw-collection}
6export CHECKBOX_SHARE=${CHECKBOX_SHARE:-.}
7export CHECKBOX_OPTIONS=${CHECKBOX_OPTIONS:---whitelist-file=$CHECKBOX_SHARE/data/whitelists/hwsubmit.whitelist}
8export PYTHONPATH=$PYTHONPATH:$CHECKBOX_SHARE
9
10if [ $CHECKBOX_DATA != '.' ]
11then
12 old_data=$HOME/.checkbox-hw-collection
13 if [ -d $old_data ] && [ ! -d $CHECKBOX_DATA ]
14 then
15 mv -f $old_data $CHECKBOX_DATA
16 fi
17fi
18
19python3 $CHECKBOX_SHARE/run "$@" $CHECKBOX_SHARE/configs/$(basename $0).ini
020
=== added file 'bin/checkbox-ocp-cli'
--- bin/checkbox-ocp-cli 1970-01-01 00:00:00 +0000
+++ bin/checkbox-ocp-cli 2013-10-01 18:38:21 +0000
@@ -0,0 +1,22 @@
1#!/bin/bash
2
3export XDG_CACHE_HOME=${XDG_CACHE_HOME:-$HOME/.cache}
4export CHECKBOX_DATA=${CHECKBOX_DATA:-.}
5export CHECKBOX_SHARE=${CHECKBOX_SHARE:-.}
6export CHECKBOX_OPTIONS=${CHECKBOX_OPTIONS:---whitelist-file=$CHECKBOX_SHARE/data/whitelists/opencompute-ready-local.whitelist}
7export PYTHONPATH=$PYTHONPATH:$CHECKBOX_SHARE
8
9if ( ! grep -q "Ubuntu" /etc/issue ) && [ ! -L /etc/checkbox.d/configs ]
10then ln -s /usr/share/checkbox/examples /etc/checkbox.d/configs
11fi
12
13if [ $CHECKBOX_DATA != '.' ]
14then
15 old_data=$HOME/.checkbox
16 if [ -d $old_data ] && [ ! -d $CHECKBOX_DATA ]
17 then
18 mv -f $old_data $CHECKBOX_DATA
19 fi
20fi
21
22python3 $CHECKBOX_SHARE/run "$@" $CHECKBOX_SHARE/configs/$(basename $0).ini
023
=== added file 'bin/checkbox-ocp-gtk'
--- bin/checkbox-ocp-gtk 1970-01-01 00:00:00 +0000
+++ bin/checkbox-ocp-gtk 2013-10-01 18:38:21 +0000
@@ -0,0 +1,22 @@
1#!/bin/bash
2
3export XDG_CACHE_HOME=${XDG_CACHE_HOME:-$HOME/.cache}
4export CHECKBOX_DATA=${CHECKBOX_DATA:-.}
5export CHECKBOX_SHARE=${CHECKBOX_SHARE:-.}
6export CHECKBOX_OPTIONS=${CHECKBOX_OPTIONS:---whitelist-file=$CHECKBOX_SHARE/data/whitelists/opencompute-ready-local.whitelist}
7export PYTHONPATH=$PYTHONPATH:$CHECKBOX_SHARE
8
9if ( ! grep -q "Ubuntu" /etc/issue ) && [ ! -L /etc/checkbox.d/configs ]
10then ln -s /usr/share/checkbox/examples /etc/checkbox.d/configs
11fi
12
13if [ $CHECKBOX_DATA != '.' ]
14then
15 old_data=$HOME/.checkbox
16 if [ -d $old_data ] && [ ! -d $CHECKBOX_DATA ]
17 then
18 mv -f $old_data $CHECKBOX_DATA
19 fi
20fi
21
22python3 $CHECKBOX_SHARE/run "$@" $CHECKBOX_SHARE/configs/$(basename $0).ini
023
=== added file 'bin/checkbox-ocp-qt'
--- bin/checkbox-ocp-qt 1970-01-01 00:00:00 +0000
+++ bin/checkbox-ocp-qt 2013-10-01 18:38:21 +0000
@@ -0,0 +1,22 @@
1#!/bin/bash
2
3export XDG_CACHE_HOME=${XDG_CACHE_HOME:-$HOME/.cache}
4export CHECKBOX_DATA=${CHECKBOX_DATA:-.}
5export CHECKBOX_SHARE=${CHECKBOX_SHARE:-.}
6export CHECKBOX_OPTIONS=${CHECKBOX_OPTIONS:---whitelist-file=$CHECKBOX_SHARE/data/whitelists/opencompute-ready-local.whitelist}
7export PYTHONPATH=$PYTHONPATH:$CHECKBOX_SHARE
8
9if ( ! grep -q "Ubuntu" /etc/issue ) && [ ! -L /etc/checkbox.d/configs ]
10then ln -s /usr/share/checkbox/examples /etc/checkbox.d/configs
11fi
12
13if [ $CHECKBOX_DATA != '.' ]
14then
15 old_data=$HOME/.checkbox
16 if [ -d $old_data ] && [ ! -d $CHECKBOX_DATA ]
17 then
18 mv -f $old_data $CHECKBOX_DATA
19 fi
20fi
21
22python3 $CHECKBOX_SHARE/run "$@" $CHECKBOX_SHARE/configs/$(basename $0).ini
023
=== added file 'bin/checkbox-ocp-urwid'
--- bin/checkbox-ocp-urwid 1970-01-01 00:00:00 +0000
+++ bin/checkbox-ocp-urwid 2013-10-01 18:38:21 +0000
@@ -0,0 +1,22 @@
1#!/bin/bash
2
3export XDG_CACHE_HOME=${XDG_CACHE_HOME:-$HOME/.cache}
4export CHECKBOX_DATA=${CHECKBOX_DATA:-.}
5export CHECKBOX_SHARE=${CHECKBOX_SHARE:-.}
6export CHECKBOX_OPTIONS=${CHECKBOX_OPTIONS:---whitelist-file=$CHECKBOX_SHARE/data/whitelists/opencompute-ready-local.whitelist}
7export PYTHONPATH=$PYTHONPATH:$CHECKBOX_SHARE
8
9if ( ! grep -q "Ubuntu" /etc/issue ) && [ ! -L /etc/checkbox.d/configs ]
10then ln -s /usr/share/checkbox/examples /etc/checkbox.d/configs
11fi
12
13if [ $CHECKBOX_DATA != '.' ]
14then
15 old_data=$HOME/.checkbox
16 if [ -d $old_data ] && [ ! -d $CHECKBOX_DATA ]
17 then
18 mv -f $old_data $CHECKBOX_DATA
19 fi
20fi
21
22python3 $CHECKBOX_SHARE/run "$@" $CHECKBOX_SHARE/configs/$(basename $0).ini
023
=== added directory 'checkbox'
=== added file 'checkbox/__init__.py'
=== added file 'checkbox/application.py'
--- checkbox/application.py 1970-01-01 00:00:00 +0000
+++ checkbox/application.py 2013-10-01 18:38:21 +0000
@@ -0,0 +1,136 @@
1#
2# This file is part of Checkbox.
3#
4# Copyright 2008 Canonical Ltd.
5#
6# Checkbox is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# Checkbox is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
18#
19import os
20import sys
21import logging
22import posixpath
23
24from gettext import gettext as _
25
26from optparse import OptionParser
27
28from checkbox.lib.config import Config
29from checkbox.lib.environ import get_variable
30from checkbox.lib.log import set_logging
31from checkbox.lib.safe import safe_make_directory
32from checkbox.lib.text import split
33
34from checkbox.plugin import PluginManager
35from checkbox.reactor import Reactor
36
37
38class Application:
39
40 reactor_factory = Reactor
41
42 def __init__(self, config):
43 self._config = config
44 self.reactor = self.reactor_factory()
45
46 # Plugin manager setup
47 self.plugin_manager = PluginManager(self._config,
48 self.reactor)
49
50 def run(self):
51 try:
52 self.reactor.run()
53 except:
54 logging.exception("Error running reactor.")
55 raise
56
57
58class ApplicationManager:
59
60 application_factory = Application
61
62 default_log = os.path.join(get_variable("CHECKBOX_DATA", "."), "checkbox.log")
63 default_log_level = "info"
64
65 def parse_options(self, args):
66 usage = _("Usage: checkbox [OPTIONS]")
67 parser = OptionParser(usage=usage)
68 parser.add_option("--version",
69 action="store_true",
70 help=_("Print version information and exit."))
71 parser.add_option("-l", "--log",
72 metavar="FILE",
73 default=self.default_log,
74 help=_("The file to write the log to."))
75 parser.add_option("--log-level",
76 default=self.default_log_level,
77 help=_("One of debug, info, warning, error or critical."))
78 parser.add_option("-c", "--config",
79 action="append",
80 type="string",
81 default=[],
82 help=_("Configuration override parameters."))
83 parser.add_option("-b", "--blacklist",
84 help=_("Shorthand for --config=.*/jobs_info/blacklist."))
85 parser.add_option("-B", "--blacklist-file",
86 help=_("Shorthand for --config=.*/jobs_info/blacklist_file."))
87 parser.add_option("-w", "--whitelist",
88 help=_("Shorthand for --config=.*/jobs_info/whitelist."))
89 parser.add_option("-W", "--whitelist-file",
90 help=_("Shorthand for --config=.*/jobs_info/whitelist_file."))
91 return parser.parse_args(args)
92
93 def create_application(self, args=sys.argv):
94 # Create data directory
95 data_directory = get_variable("CHECKBOX_DATA", ".")
96 safe_make_directory(data_directory)
97
98 # Prepend environment options
99 string_options = get_variable("CHECKBOX_OPTIONS", "")
100 args[:0] = split(string_options)
101 (options, args) = self.parse_options(args)
102
103 # Replace shorthands
104 for shorthand in "blacklist", "blacklist_file", "whitelist", "whitelist_file":
105 key = ".*/jobs_info/%s" % shorthand
106 value = getattr(options, shorthand)
107 if value:
108 options.config.append("=".join([key, value]))
109
110 # Set logging early
111 set_logging(options.log_level, options.log)
112
113 # Config setup
114 if len(args) != 2:
115 sys.stderr.write(_("Missing configuration file as argument.\n"))
116 sys.exit(1)
117
118 config = Config()
119 config_filename = posixpath.expanduser(args[1])
120 config.read_filename(config_filename)
121 config.read_configs(options.config)
122
123 section_name = "checkbox/plugins/client_info"
124 section = config.get_section(section_name)
125 if not section:
126 section = config.add_section(section_name)
127 section.set("name", posixpath.basename(args[1]) \
128 .replace(".ini", ""))
129 section.set("version", config.get_defaults().version)
130
131 # Check options
132 if options.version:
133 print(config.get_defaults().version)
134 sys.exit(0)
135
136 return self.application_factory(config)
0137
=== added file 'checkbox/arguments.py'
--- checkbox/arguments.py 1970-01-01 00:00:00 +0000
+++ checkbox/arguments.py 2013-10-01 18:38:21 +0000
@@ -0,0 +1,95 @@
1#
2# This file is part of Checkbox.
3#
4# Copyright 2008 Canonical Ltd.
5#
6# Checkbox is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# Checkbox is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
18#
19from inspect import getargspec
20
21from checkbox.lib.decorator import merge_function_metadata
22
23
24class ArgumentReplacer:
25 """A decorator helper that filters arguments to be passed to a function.
26
27 Create one with the original function and a function to replace
28 the arguments, and then call replace_arguments for each call to
29 the function.
30 """
31 def __init__(self, original_function, argument_replacer):
32 """
33 @param original_function: The function which will be called
34 soon. The function must *not* define any * or ** parameters.
35 @param argument_replacer: A function which will be called for
36 every argument. It will be passed a parameter name and the
37 associated argument. It should return the new value.
38 """
39 self.original_function = original_function
40 self.argument_replacer = argument_replacer
41
42 spec = getargspec(original_function)
43 self.all_arguments = spec[0]
44 if getattr(original_function, 'im_self', None) is not None:
45 # If it's bound, ignore the bound arguments.
46 self.all_arguments = self.all_arguments[1:]
47
48 def replace_arguments(self, args, kwargs):
49 """Filter some arguments destined to be passed to a function.
50
51 @param args: Original positional arguments.
52 @param kwargs: Original keyword arguments.
53
54 @return: new arguments and kwarguments.
55 """
56 args = list(args)
57 kwargs = kwargs.copy()
58
59 for name_index, name in enumerate(self.all_arguments):
60 # Ok, we've got the name of the argument. Let's find the value
61 # of the argument in our original arguments and replace it
62 # whether it's a positional or a keyword argument.
63 if name_index < len(args):
64 # Must be a positional argument
65 value = args[name_index]
66 args[name_index] = self.argument_replacer(name, value)
67 else:
68 # Must be a keyword argument
69 if name not in kwargs:
70 # Oh, but it wasn't passed in. Ignore it.
71 continue
72 value = kwargs[name]
73 kwargs[name] = self.argument_replacer(name, value)
74 return args, kwargs
75
76
77def coerce_arguments(**schemas):
78 """
79 A decorator factory which returns a decorator which coerces arguments.
80 """
81 def replacer(name, value):
82 if name in schemas:
83 return schemas[name].coerce(value)
84 return value
85
86 def decorator(original):
87 argument_replacer = ArgumentReplacer(original, replacer)
88 def replacement(*args, **kwargs):
89 new_args, new_kwargs = argument_replacer.replace_arguments(args,
90 kwargs)
91 return original(*new_args, **new_kwargs)
92 merge_function_metadata(original, replacement)
93 return replacement
94
95 return decorator
096
=== added file 'checkbox/attribute.py'
--- checkbox/attribute.py 1970-01-01 00:00:00 +0000
+++ checkbox/attribute.py 2013-10-01 18:38:21 +0000
@@ -0,0 +1,41 @@
1#
2# This file is part of Checkbox.
3#
4# Copyright 2008 Canonical Ltd.
5#
6# Checkbox is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# Checkbox is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
18#
19from checkbox.variables import Variable
20
21
22class Attribute:
23
24 def __init__(self, name, cls, variable_factory=None):
25 self.name = name
26 self.cls = cls
27 self.variable_factory = variable_factory or Variable
28
29
30def get_attributes(cls):
31 if "__checkbox_attributes__" in cls.__dict__:
32 return cls.__dict__["__checkbox_attributes__"]
33 else:
34 attributes = {}
35 for name in dir(cls):
36 attribute = getattr(cls, name, None)
37 if isinstance(attribute, Attribute):
38 attributes[name] = attribute
39
40 cls.__checkbox_attributes__ = attributes
41 return cls.__checkbox_attributes__
042
=== added file 'checkbox/component.py'
--- checkbox/component.py 1970-01-01 00:00:00 +0000
+++ checkbox/component.py 2013-10-01 18:38:21 +0000
@@ -0,0 +1,184 @@
1#
2# This file is part of Checkbox.
3#
4# Copyright 2008 Canonical Ltd.
5#
6# Checkbox is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# Checkbox is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
18#
19import os
20import re
21import itertools
22import logging
23import posixpath
24
25from checkbox.lib.cache import cache
26from checkbox.lib.path import path_expand_recursive
27
28from checkbox.properties import List, String
29from checkbox.variables import get_variables
30
31
32class ComponentSection:
33 """
34 Component section which is essentially a container of modules. These
35 map to the modules referenced in the configuration passed as argument
36 to the constructor.
37 """
38
39 modules = List(type=String())
40 whitelist = List(type=String(), default_factory=lambda:"")
41 blacklist = List(type=String(), default_factory=lambda:"")
42
43 def __init__(self, config, name):
44 """
45 Constructor which takes a configuration instance and name as
46 argument. The former is expected to contain modules.
47 """
48 self._config = config
49 self.name = name
50
51 self.modules = config.modules
52 self.whitelist = config.get("whitelist")
53 self.blacklist = config.get("blacklist")
54
55 @cache
56 def get_names(self):
57 """
58 Get all the module names contained in the filenames or directories
59 for this section.
60 """
61 whitelist_patterns = [re.compile(r"^%s$" % r) for r in self.whitelist]
62 blacklist_patterns = [re.compile(r"^%s$" % r) for r in self.blacklist]
63
64 # Determine names
65 names = set()
66 filenames = itertools.chain(*[path_expand_recursive(m)
67 for m in self.modules])
68 for filename in filenames:
69 name = posixpath.basename(filename)
70 if not name.endswith(".py") or name == "__init__.py":
71 # Ignore silently
72 continue
73
74 name = name.replace(".py", "")
75 if whitelist_patterns:
76 if not [name for p in whitelist_patterns if p.match(name)]:
77 logging.info("Not whitelisted module: %s", name)
78 continue
79 elif blacklist_patterns:
80 if [name for p in blacklist_patterns if p.match(name)]:
81 logging.info("Blacklisted module: %s", name)
82 continue
83
84 names.add(name)
85
86 return list(names)
87
88 def has_module(self, name):
89 """
90 Check if the given name is in this section.
91 """
92 return name in self.get_names()
93
94 def load_module(self, name):
95 """
96 Load a single module by name from this section.
97 """
98 logging.info("Loading module %s from section %s",
99 name, self.name)
100
101 if not self.has_module(name):
102 raise Exception("No such such module: %s" % name)
103
104 filenames = itertools.chain(*[path_expand_recursive(m)
105 for m in self.modules])
106 for filename in filenames:
107 if filename.endswith(".py") and posixpath.exists(filename):
108 basename = posixpath.basename(filename)
109 basename = basename.replace(".py", "")
110 if basename == name:
111 globals = {}
112 exec(open(filename).read(), globals)
113 if "factory" not in globals:
114 raise Exception("Variable 'factory' not found in: %s" \
115 % filename)
116
117 module = globals["factory"]()
118 module.__module__ = name
119
120 config_name = "/".join([self.name, name])
121 config = self._config.parent.get_section(config_name)
122
123 # Set configuration values
124 variables = get_variables(module)
125 environ = dict([(k.lower(), v) for k, v in os.environ.items()])
126 for attribute, variable in variables.items():
127 if config and attribute.name in config:
128 value = config.get(attribute.name)
129 variable.set(value)
130 else:
131 value = variable.get()
132 if isinstance(value, str):
133 value = value % environ
134 variable.set(value)
135 elif isinstance(value, list):
136 value = [v % environ for v in value]
137 variable.set(value)
138
139 # Check required attributes
140 for attribute, variable in variables.items():
141 value = variable.get()
142 if value is None and variable._required:
143 raise Exception("Configuration '%s' missing " \
144 "required attribute: %s" \
145 % (config_name, attribute.name))
146
147 return module
148
149 raise Exception("Failed to find module '%s' in: %s" % (name, filenames))
150
151 def load_modules(self):
152 """
153 Load all modules contained in this section.
154 """
155 modules = []
156 for name in self.get_names():
157 module = self.load_module(name)
158 modules.append(module)
159
160 return modules
161
162
163class ComponentManager:
164 """
165 Component manager which is essentially a container of sections.
166 """
167
168 _section_factory = ComponentSection
169
170 def __init__(self, config):
171 """
172 Constructor which takes a configuration instance as argument. This
173 will be used to load sections by name.
174 """
175 self._config = config
176
177 def load_section(self, name):
178 """
179 Load a section by name which must correspond to a module in the
180 configuration instance pased as argument to the constructor.
181 """
182 logging.info("Loading component section %s", name)
183 config = self._config.get_section(name)
184 return self._section_factory(config, name)
0185
=== added directory 'checkbox/contrib'
=== added file 'checkbox/contrib/REThread.py'
--- checkbox/contrib/REThread.py 1970-01-01 00:00:00 +0000
+++ checkbox/contrib/REThread.py 2013-10-01 18:38:21 +0000
@@ -0,0 +1,142 @@
1'''Enhanced threading.Thread which can deliver a return value and propagate
2exceptions from the called thread to the calling thread.
3
4Copyright (C) 2007 Canonical Ltd.
5Author: Martin Pitt <martin.pitt@ubuntu.com>
6
7This program is free software; you can redistribute it and/or modify it
8under the terms of the GNU General Public License as published by the
9Free Software Foundation; either version 2 of the License, or (at your
10option) any later version. See http://www.gnu.org/copyleft/gpl.html for
11the full text of the license.
12'''
13
14import threading, sys
15
16class REThread(threading.Thread):
17 '''Enhanced threading.Thread which can deliver a return value and propagate
18 exceptions from the called thread to the calling thread.'''
19
20 def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
21 verbose=None):
22 '''Initialize Thread, identical to threading.Thread.__init__().'''
23 #Note that due to api differences in python (verbose disappeared
24 #at some point before 3.3 and got replaced by daemon), we just
25 #ignore the verbose attribute. It's not used anywhere in checkbox
26 #so this is safe for our purposes.
27 self.__target = target
28 self.__args = args
29 self.__kwargs = kwargs
30 self._retval = None
31 self._exception = None
32
33 threading.Thread.__init__(self)
34
35 def run(self):
36 '''Run target function, identical to threading.Thread.run().'''
37
38 if self.__target:
39 try:
40 self._retval = self.__target(*self.__args, **self.__kwargs)
41 except:
42 self._exception = sys.exc_info()
43
44 def return_value(self):
45 '''Return value from target function.
46
47 This can only be called after the thread has finished, i. e. when
48 isAlive() is False and did not terminate with an exception.'''
49
50 assert not self.isAlive()
51 assert not self._exception
52 return self._retval
53
54 def exc_info(self):
55 '''Return a tuple (type, value, traceback) of the exception caught in
56 run().'''
57
58 return self._exception
59
60 def exc_raise(self):
61 '''Raises the exception caught in the thread.
62
63 Does nothing if no exception was caught.'''
64
65 if self._exception:
66 raise self._exception[0](self._exception[1]).with_traceback(self._exception[2])
67
68#
69# Unit test
70#
71
72if __name__ == '__main__':
73 import unittest, time, traceback, exceptions
74
75 def idle(seconds):
76 '''Test thread to just wait a bit.'''
77
78 time.sleep(seconds)
79
80 def div(x, y):
81 '''Test thread to divide two numbers.'''
82
83 return x / y
84
85 class _REThreadTest(unittest.TestCase):
86 def test_return_value(self):
87 '''Test that return value works properly.'''
88
89 t = REThread(target=div, args=(42, 2))
90 t.start()
91 t.join()
92 # exc_raise() should be a no-op on successful functions
93 t.exc_raise()
94 self.assertEqual(t.return_value(), 21)
95 self.assertEqual(t.exc_info(), None)
96
97 def test_no_return_value(self):
98 '''Test that REThread works if run() does not return anything.'''
99
100 t = REThread(target=idle, args=(0.5,))
101 t.start()
102 # thread must be joined first
103 self.assertRaises(AssertionError, t.return_value)
104 t.join()
105 self.assertEqual(t.return_value(), None)
106 self.assertEqual(t.exc_info(), None)
107
108 def test_exception(self):
109 '''Test that exception in thread is caught and passed.'''
110
111 t = REThread(target=div, args=(1, 0))
112 t.start()
113 t.join()
114 # thread did not terminate normally, no return value
115 self.assertRaises(AssertionError, t.return_value)
116 self.assert_(t.exc_info()[0] == exceptions.ZeroDivisionError)
117 exc = traceback.format_exception(t.exc_info()[0], t.exc_info()[1],
118 t.exc_info()[2])
119 self.assert_(exc[-1].startswith('ZeroDivisionError'))
120 self.assert_(exc[-2].endswith('return x / y\n'))
121
122 def test_exc_raise(self):
123 '''Test that exc_raise() raises caught thread exception.'''
124
125 t = REThread(target=div, args=(1, 0))
126 t.start()
127 t.join()
128 # thread did not terminate normally, no return value
129 self.assertRaises(AssertionError, t.return_value)
130 raised = False
131 try:
132 t.exc_raise()
133 except:
134 raised = True
135 e = sys.exc_info()
136 exc = traceback.format_exception(e[0], e[1], e[2])
137 self.assert_(exc[-1].startswith('ZeroDivisionError'))
138 self.assert_(exc[-2].endswith('return x / y\n'))
139 self.assert_(raised)
140
141 unittest.main()
142
0143
=== added file 'checkbox/contrib/__init__.py'
=== added file 'checkbox/contrib/bpickle.py'
--- checkbox/contrib/bpickle.py 1970-01-01 00:00:00 +0000
+++ checkbox/contrib/bpickle.py 2013-10-01 18:38:21 +0000
@@ -0,0 +1,188 @@
1"""
2Copyright (c) 2006, Gustavo Niemeyer <gustavo@niemeyer.net>
3
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without
7modification, are permitted provided that the following conditions are met:
8
9 * Redistributions of source code must retain the above copyright notice,
10 this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright notice,
12 this list of conditions and the following disclaimer in the documentation
13 and/or other materials provided with the distribution.
14 * Neither the name of the copyright holder nor the names of its
15 contributors may be used to endorse or promote products derived from
16 this software without specific prior written permission.
17
18THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29"""
30
31
32dumps_table = {}
33loads_table = {}
34
35
36def dumps(obj, _dt=None):
37 if not _dt:
38 _dt = dumps_table
39
40 type_names = [type(obj)]
41 for type_name in type_names:
42 if type_name in _dt:
43 return _dt[type_name](obj)
44
45 type_names.extend(type_name.__bases__)
46
47 raise ValueError("Unsupported type: %s" % type(obj))
48
49
50def loads(str, _lt=loads_table):
51 if not str:
52 raise ValueError("Can't load empty string")
53 try:
54 return _lt[str[0]](str, 0)[0]
55 except KeyError as e:
56 raise ValueError("Unknown type character: %s" % e)
57 except IndexError:
58 raise ValueError("Corrupted data")
59
60
61def dumps_bool(obj):
62 return ("b%d" % int(obj)).encode("ascii")
63
64
65def dumps_int(obj):
66 return ("i%s;" % obj).encode("ascii")
67
68
69def dumps_float(obj):
70 return ("f%r;" % obj).encode("ascii")
71
72
73def dumps_bytes(obj):
74 return ("c%s:" % len(obj)).encode("ascii") + obj
75
76
77def dumps_str(obj):
78 obj = obj.encode("utf-8")
79 return ("s%s:" % len(obj)).encode("ascii") + obj
80
81
82def dumps_list(obj, _dt=None):
83 return b"l" + b"".join([dumps(val, _dt) for val in obj]) + b";"
84
85
86def dumps_tuple(obj, _dt=None):
87 return b"t" + b"".join([dumps(val, _dt) for val in obj]) + b";"
88
89
90def dumps_dict(obj, _dt=None):
91 res = []
92 keys = sorted(obj.keys())
93 append = res.append
94 for key in keys:
95 val = obj[key]
96 append(dumps(key, _dt))
97 append(dumps(val, _dt))
98 return b"d" + b"".join(res) + b";"
99
100
101def dumps_none(obj):
102 return b"n"
103
104
105def loads_bool(str, pos):
106 return bool(int(chr(str[pos + 1]))), pos + 2
107
108
109def loads_int(str, pos):
110 endpos = str.index(b";", pos)
111 return int(str[pos + 1:endpos]), endpos + 1
112
113
114def loads_float(str, pos):
115 endpos = str.index(b";", pos)
116 return float(str[pos + 1:endpos]), endpos + 1
117
118
119def loads_bytes(str, pos):
120 startpos = str.index(b":", pos) + 1
121 endpos = startpos + int(str[pos + 1:startpos - 1])
122 return str[startpos:endpos], endpos
123
124
125def loads_str(str, pos):
126 startpos = str.index(b":", pos) + 1
127 endpos = startpos + int(str[pos + 1:startpos - 1])
128 return str[startpos:endpos].decode("utf-8"), endpos
129
130
131def loads_list(str, pos, _lt=loads_table):
132 pos += 1
133 res = []
134 append = res.append
135 while str[pos] != ord(";"):
136 obj, pos = _lt[str[pos]](str, pos)
137 append(obj)
138 return res, pos + 1
139
140
141def loads_tuple(str, pos, _lt=loads_table):
142 pos += 1
143 res = []
144 append = res.append
145 while str[pos] != ord(";"):
146 obj, pos = _lt[str[pos]](str, pos)
147 append(obj)
148 return tuple(res), pos + 1
149
150
151def loads_dict(str, pos, _lt=loads_table):
152 pos += 1
153 res = {}
154 while str[pos] != ord(";"):
155 key, pos = _lt[str[pos]](str, pos)
156 val, pos = _lt[str[pos]](str, pos)
157 res[key] = val
158 return res, pos + 1
159
160
161def loads_none(str, pos):
162 return None, pos + 1
163
164
165dumps_table.update({
166 bool: dumps_bool,
167 int: dumps_int,
168 float: dumps_float,
169 bytes: dumps_bytes,
170 str: dumps_str,
171 list: dumps_list,
172 tuple: dumps_tuple,
173 dict: dumps_dict,
174 type(None): dumps_none,
175 })
176
177loads_table.update({
178 ord("b"): loads_bool,
179 ord("i"): loads_int,
180 ord("f"): loads_float,
181 ord("c"): loads_str,
182 ord("s"): loads_str,
183 ord("u"): loads_str,
184 ord("l"): loads_list,
185 ord("t"): loads_tuple,
186 ord("d"): loads_dict,
187 ord("n"): loads_none,
188 })
0189
=== added file 'checkbox/contrib/gdk.py'
--- checkbox/contrib/gdk.py 1970-01-01 00:00:00 +0000
+++ checkbox/contrib/gdk.py 2013-10-01 18:38:21 +0000
@@ -0,0 +1,46 @@
1#!/usr/bin/python3
2# -*- coding: utf-8 -*-
3#
4# Python-XRandR provides a high level API for the XRandR extension of the
5# X.org server. XRandR allows to configure resolution, refresh rate, rotation
6# of the screen and multiple outputs of graphics cards.
7#
8# This module allows to get information for gtk.gdk.Screen objects
9#
10# In many aspects it follows the design of the xrand tool written by
11# Keith Packard.
12#
13# Copyright 2007 © Sebastian Heinlein <sebastian.heinlein@web.de>
14#
15# This library is free software; you can redistribute it and/or
16# modify it under the terms of the GNU Lesser General Public
17# License as published by the Free Software Foundation; either
18# version 2.1 of the License, or any later version.
19#
20# This library is distributed in the hope that it will be useful,
21# but WITHOUT ANY WARRANTY; without even the implied warranty of
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23# Lesser General Public License for more details.
24#
25# You should have received a copy of the GNU Lesser General Public
26# License along with this library; if not, write to the Free Software
27# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
28
29from gi.repository import Gdk
30from checkbox.contrib import xrandr
31
32#FIXME: Perhaps using gdk_x11_screen_get_xscreen would be more elegant
33
34def get_default_screen_config():
35 dpy = Gdk.Display.get_default()
36 screen = Gdk.Display.get_default_screen(dpy)
37 return get_screen_config(screen)
38
39def get_screen_config(screen):
40 """Returns the XRandR screen config instance for the given gtk.gdk.Screen"""
41 dpy = screen.get_display()
42 dpy_url = dpy.get_name()
43 count = screen.get_number()
44 return xrandr.get_screen_of_display(dpy_url, count)
45
46# vim:ts=4:sw=4:et
047
=== added file 'checkbox/contrib/glock.py'
--- checkbox/contrib/glock.py 1970-01-01 00:00:00 +0000
+++ checkbox/contrib/glock.py 2013-10-01 18:38:21 +0000
@@ -0,0 +1,311 @@
1#!/usr/bin/env python3
2# -*- coding: latin1 -*-
3#----------------------------------------------------------------------------
4# glock.py: Global mutex
5#
6# See __doc__ string below.
7#
8# Requires:
9# - Python 1.5.2 or newer (www.python.org)
10# - On windows: win32 extensions installed
11# (http://www.python.org/windows/win32all/win32all.exe)
12# - OS: Unix, Windows.
13#
14# $Id: //depot/rgutils/rgutils/glock.py#5 $
15#----------------------------------------------------------------------------
16'''
17This module defines the class GlobalLock that implements a global
18(inter-process) mutex on Windows and Unix, using file-locking on
19Unix.
20
21@see: class L{GlobalLock} for more details.
22'''
23__version__ = '0.2.' + '$Revision: #5 $'[12:-2]
24__author__ = 'Richard Gruet', 'rjgruet@yahoo.com'
25__date__ = '$Date: 2005/06/19 $'[7:-2], '$Author: rgruet $'[9:-2]
26__since__ = '2000-01-22'
27__doc__ += '\n@author: %s (U{%s})\n@version: %s' % (__author__[0],
28 __author__[1], __version__)
29__all__ = ['GlobalLock', 'GlobalLockError', 'LockAlreadyAcquired', 'NotOwner']
30
31# Imports:
32import sys, string, os, errno, re, posixpath
33
34# System-dependent imports for locking implementation:
35_windows = (sys.platform == 'win32')
36
37if _windows:
38 try:
39 import win32event, win32api, pywintypes
40 except ImportError:
41 sys.stderr.write('The win32 extensions need to be installed!')
42 try:
43 import ctypes
44 except ImportError:
45 ctypes = None
46else: # assume Unix
47 try:
48 import fcntl
49 except ImportError:
50 sys.stderr.write("On what kind of OS am I ? (Mac?) I should be on "
51 "Unix but can't import fcntl.\n")
52 raise
53 import threading
54
55# Exceptions :
56# ----------
57class GlobalLockError(Exception):
58 ''' Error raised by the glock module.
59 '''
60 pass
61
62class NotOwner(GlobalLockError):
63 ''' Attempt to release somebody else's lock.
64 '''
65 pass
66
67class LockAlreadyAcquired(GlobalLockError):
68 ''' Non-blocking acquire but lock already seized.
69 '''
70 pass
71
72
73#----------------------------------------------------------------------------
74class GlobalLock:
75#----------------------------------------------------------------------------
76 ''' A global mutex.
77
78 B{Specification}
79
80 - The lock must act as a global mutex, ie block between different
81 candidate processus, but ALSO between different candidate
82 threads of the same process.
83
84 - It must NOT block in case of reentrant lock request issued by
85 the SAME thread.
86 - Extraneous unlocks should be ideally harmless.
87
88 B{Implementation}
89
90 In Python there is no portable global lock AFAIK. There is only a
91 LOCAL/ in-process Lock mechanism (threading.RLock), so we have to
92 implement our own solution:
93
94 - Unix: use fcntl.flock(). Recursive calls OK. Different process OK.
95 But <> threads, same process don't block so we have to use an extra
96 threading.RLock to fix that point.
97 - Windows: We use WIN32 mutex from Python Win32 extensions. Can't use
98 std module msvcrt.locking(), because global lock is OK, but
99 blocks also for 2 calls from the same thread!
100 '''
101 RE_ERROR_MSG = re.compile ("^\[Errno ([0-9]+)\]")
102
103 def __init__(self, fpath, lockInitially=False, logger=None):
104 ''' Creates (or opens) a global lock.
105
106 @param fpath: Path of the file used as lock target. This is also
107 the global id of the lock. The file will be created
108 if non existent.
109 @param lockInitially: if True locks initially.
110 @param logger: an optional logger object.
111 '''
112 self.logger = logger
113 self.fpath = fpath
114 if posixpath.exists(fpath):
115 self.previous_lockfile_present = True
116 else:
117 self.previous_lockfile_present = False
118 if _windows:
119 self.name = string.replace(fpath, '\\', '_')
120 self.mutex = win32event.CreateMutex(None, lockInitially, self.name)
121 else: # Unix
122 self.name = fpath
123 self.flock = open(fpath, 'w')
124 self.fdlock = self.flock.fileno()
125 self.threadLock = threading.RLock()
126 if lockInitially:
127 self.acquire()
128
129 def __del__(self):
130 #print '__del__ called' ##
131 try: self.release()
132 except: pass
133 if _windows:
134 win32api.CloseHandle(self.mutex)
135 else:
136 try: self.flock.close()
137 except: pass
138
139 def __repr__(self):
140 return '<Global lock @ %s>' % self.name
141
142 def acquire(self, blocking=False):
143 """ Locks. Attemps to acquire a lock.
144
145 @param blocking: If True, suspends caller until done. Otherwise,
146 LockAlreadyAcquired is raised if the lock cannot be acquired immediately.
147
148 On windows an IOError is always raised after ~10 sec if the lock
149 can't be acquired.
150 @exception GlobalLockError: if lock can't be acquired (timeout)
151 @exception LockAlreadyAcquired: someone already has the lock and
152 the caller decided not to block.
153 """
154 if self.logger:
155 self.logger.info('creating lockfile')
156 if _windows:
157 if blocking:
158 timeout = win32event.INFINITE
159 else:
160 timeout = 0
161 r = win32event.WaitForSingleObject(self.mutex, timeout)
162 if r == win32event.WAIT_FAILED:
163 raise GlobalLockError("Can't acquire mutex: error")
164 if not blocking and r == win32event.WAIT_TIMEOUT:
165 raise LockAlreadyAcquired('Lock %s already acquired by '
166 'someone else' % self.name)
167 else:
168 # First, acquire the global (inter-process) lock:
169 if blocking:
170 options = fcntl.LOCK_EX
171 else:
172 options = fcntl.LOCK_EX|fcntl.LOCK_NB
173 try:
174 fcntl.flock(self.fdlock, options)
175 except IOError as message: #(errno 13: perm. denied,
176 # 36: Resource deadlock avoided)
177 if not blocking and self._errnoOf (message) == errno.EWOULDBLOCK:
178 raise LockAlreadyAcquired('Lock %s already acquired by '
179 'someone else' % self.name)
180 else:
181 raise GlobalLockError('Cannot acquire lock on "file" '
182 '%s: %s\n' % (self.name, message))
183 #print 'got file lock.' ##
184
185 # Then acquire the local (inter-thread) lock:
186 if not self.threadLock.acquire(blocking):
187 fcntl.flock(self.fdlock, fcntl.LOCK_UN) # release global lock
188 raise LockAlreadyAcquired('Lock %s already acquired by '
189 'someone else' % self.name)
190 if self.previous_lockfile_present and self.logger:
191 self.logger.warn("Stale lockfile detected and claimed.")
192 #print 'got thread lock.' ##
193
194 self.is_locked = True
195
196 def release(self, skip_delete=False):
197 ''' Unlocks. (caller must own the lock!)
198
199 @param skip_delete: don't try to delete the file. This can
200 be used when the original filename has changed; for
201 instance, if the lockfile is erased out-of-band, or if
202 the directory it contains has been renamed.
203
204 @return: The lock count.
205 @exception IOError: if file lock can't be released
206 @exception NotOwner: Attempt to release somebody else's lock.
207 '''
208 if not self.is_locked:
209 return
210 if not skip_delete:
211 if self.logger:
212 self.logger.debug('Removing lock file: %s', self.fpath)
213 os.unlink(self.fpath)
214 elif self.logger:
215 # At certain times the lockfile will have been removed or
216 # moved away before we call release(); log a message because
217 # this is unusual and could be an error.
218 self.logger.debug('Oops, my lock file disappeared: %s', self.fpath)
219 if _windows:
220 if ctypes:
221 result = ctypes.windll.kernel32.ReleaseMutex(self.mutex.handle)
222 if not result:
223 raise NotOwner("Attempt to release somebody else's lock")
224 else:
225 try:
226 win32event.ReleaseMutex(self.mutex)
227 #print "released mutex"
228 except pywintypes.error as e:
229 errCode, fctName, errMsg = e.args
230 if errCode == 288:
231 raise NotOwner("Attempt to release somebody else's lock")
232 else:
233 raise GlobalLockError('%s: err#%d: %s' % (fctName, errCode,
234 errMsg))
235 else:
236 # First release the local (inter-thread) lock:
237 try:
238 self.threadLock.release()
239 except AssertionError:
240 raise NotOwner("Attempt to release somebody else's lock")
241
242 # Then release the global (inter-process) lock:
243 try:
244 fcntl.flock(self.fdlock, fcntl.LOCK_UN)
245 except IOError: # (errno 13: permission denied)
246 raise GlobalLockError('Unlock of file "%s" failed\n' %
247 self.name)
248 self.is_locked = False
249
250 def _errnoOf (self, message):
251 match = self.RE_ERROR_MSG.search(str(message))
252 if match:
253 return int(match.group(1))
254 else:
255 raise Exception ('Malformed error message "%s"' % message)
256
257#----------------------------------------------------------------------------
258def test():
259#----------------------------------------------------------------------------
260 ##TODO: a more serious test with distinct processes !
261
262 print('Testing glock.py...')
263
264 # unfortunately can't test inter-process lock here!
265 lockName = 'myFirstLock'
266 l = GlobalLock(lockName)
267 if not _windows:
268 assert posixpath.exists(lockName)
269 l.acquire()
270 l.acquire() # reentrant lock, must not block
271 l.release()
272 l.release()
273
274 try: l.release()
275 except NotOwner: pass
276 else: raise Exception('should have raised a NotOwner exception')
277
278 # Check that <> threads of same process do block:
279 import threading, time
280 thread = threading.Thread(target=threadMain, args=(l,))
281 print('main: locking...', end=' ')
282 l.acquire()
283 print(' done.')
284 thread.start()
285 time.sleep(3)
286 print('\nmain: unlocking...', end=' ')
287 l.release()
288 print(' done.')
289 time.sleep(0.1)
290
291 print('=> Test of glock.py passed.')
292 return l
293
294def threadMain(lock):
295 print('thread started(%s).' % lock)
296 try: lock.acquire(blocking=False)
297 except LockAlreadyAcquired: pass
298 else: raise Exception('should have raised LockAlreadyAcquired')
299 print('thread: locking (should stay blocked for ~ 3 sec)...', end=' ')
300 lock.acquire()
301 print('thread: locking done.')
302 print('thread: unlocking...', end=' ')
303 lock.release()
304 print(' done.')
305 print('thread ended.')
306
307#----------------------------------------------------------------------------
308# M A I N
309#----------------------------------------------------------------------------
310if __name__ == "__main__":
311 l = test()
0312
=== added file 'checkbox/contrib/persist.py'
--- checkbox/contrib/persist.py 1970-01-01 00:00:00 +0000
+++ checkbox/contrib/persist.py 2013-10-01 18:38:21 +0000
@@ -0,0 +1,552 @@
1# Copyright (c) 2006 Canonical
2# Copyright (c) 2004 Conectiva, Inc.
3#
4# Written by Gustavo Niemeyer <gustavo@niemeyer.net>
5#
6# This Python module is free software; you can redistribute it and/or
7# modify it under the terms of the GNU General Public License as published
8# by the Free Software Foundation; either version 2 of the License, or (at
9# your option) any later version.
10#
11# This Python module is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14# General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this Python module; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19#
20import os
21import sys
22import copy
23import re
24import posixpath
25
26from checkbox.lib.safe import safe_close
27
28__all__ = ["Persist", "MemoryBackend", "PickleBackend", "BPickleBackend",
29 "path_string_to_tuple", "path_tuple_to_string", "RootedPersist",
30 "PersistError", "PersistReadOnlyError"]
31
32
33NOTHING = object()
34
35
36class PersistError(Exception):
37 pass
38
39
40class PersistReadOnlyError(PersistError):
41 pass
42
43
44class Persist:
45 """Persistence handler.
46
47 There are three different kinds of opition maps, regarding the
48 persistence and priority that maps are queried.
49
50 hard - Options are persistent.
51 soft - Options are not persistent, and have a higher priority
52 than persistent options.
53 weak - Options are not persistent, and have a lower priority
54 than persistent options.
55
56 @ivar filename: The name of the file where persist data is saved
57 or None if not filename is available.
58 """
59
60
61 def __init__(self, filename=None, backend=None):
62 """
63 @param backend: The backend to use. If none is specified,
64 L{BPickleBackend} will be used.
65 @param filename: The default filename to save to and load from. If
66 specified, and the file exists, it will be immediately
67 loaded. Specifying this will also allow L{save} to be called
68 without any arguments to save the persist.
69 """
70 if backend is None:
71 backend = BPickleBackend()
72 self._backend = backend
73 self._hardmap = backend.new()
74 self._softmap = {}
75 self._weakmap = {}
76 self._readonly = False
77 self._modified = False
78 self.filename = filename
79 if filename is not None and posixpath.exists(filename):
80 self.load(filename)
81
82 def _get_readonly(self):
83 return self._readonly
84
85 def _set_readonly(self, flag):
86 self._readonly = bool(flag)
87
88 def _get_modified(self):
89 return self._modified
90
91 readonly = property(_get_readonly, _set_readonly)
92 modified = property(_get_modified)
93
94 def reset_modified(self):
95 self._modified = False
96
97 def assert_writable(self):
98 if self._readonly:
99 raise PersistReadOnlyError("Configuration is in readonly mode.")
100
101 def load(self, filepath):
102 filepath = posixpath.expanduser(filepath)
103 if not posixpath.isfile(filepath):
104 raise PersistError("File not found: %s" % filepath)
105 if posixpath.getsize(filepath) == 0:
106 return
107 try:
108 self._hardmap = self._backend.load(filepath)
109 except:
110 filepathold = filepath+".old"
111 if (posixpath.isfile(filepathold) and
112 posixpath.getsize(filepathold) > 0):
113 # warning("Broken configuration file at %s" % filepath)
114 # warning("Trying backup at %s" % filepathold)
115 try:
116 self._hardmap = self._backend.load(filepathold)
117 except:
118 raise PersistError("Broken configuration file at %s" %
119 filepathold)
120 else:
121 raise PersistError("Broken configuration file at %s" %
122 filepath)
123
124 def save(self, filepath=None):
125 """Save the persist to the given C{filepath}.
126
127 If None is specified, then the filename passed during construction will
128 be used.
129 """
130 if filepath is None:
131 if self.filename is None:
132 return
133 filepath = self.filename
134 filepath = posixpath.expanduser(filepath)
135 if posixpath.isfile(filepath):
136 os.rename(filepath, filepath+".old")
137 dirname = posixpath.dirname(filepath)
138 if dirname and not posixpath.isdir(dirname):
139 os.makedirs(dirname)
140 self._backend.save(filepath, self._hardmap)
141
142 def _traverse(self, obj, path, default=NOTHING, setvalue=NOTHING):
143 if setvalue is not NOTHING:
144 setvalue = self._backend.copy(setvalue)
145 queue = list(path)
146 marker = NOTHING
147 newobj = obj
148 while queue:
149 obj = newobj
150 elem = queue.pop(0)
151 newobj = self._backend.get(obj, elem)
152 if newobj is NotImplemented:
153 if queue:
154 path = path[:-len(queue)]
155 raise PersistError("Can't traverse %r (%r): %r" %
156 (type(obj), path_tuple_to_string(path),
157 str(obj)))
158 if newobj is marker:
159 break
160 if newobj is not marker:
161 if setvalue is not marker:
162 newobj = self._backend.set(obj, elem, setvalue)
163 else:
164 if setvalue is marker:
165 newobj = default
166 else:
167 while True:
168 if len(queue) > 0:
169 if type(queue[0]) is int:
170 newvalue = []
171 else:
172 newvalue = {}
173 else:
174 newvalue = setvalue
175 newobj = self._backend.set(obj, elem, newvalue)
176 if newobj is NotImplemented:
177 raise PersistError("Can't traverse %r with %r" %
178 (type(obj), type(elem)))
179 if not queue:
180 break
181 obj = newobj
182 elem = queue.pop(0)
183 return newobj
184
185 def _getvalue(self, path, soft=False, hard=False, weak=False):
186 if type(path) is str:
187 path = path_string_to_tuple(path)
188 marker = NOTHING
189 if soft:
190 value = self._traverse(self._softmap, path, marker)
191 elif hard:
192 value = self._traverse(self._hardmap, path, marker)
193 elif weak:
194 value = self._traverse(self._weakmap, path, marker)
195 else:
196 value = self._traverse(self._softmap, path, marker)
197 if value is marker:
198 value = self._traverse(self._hardmap, path, marker)
199 if value is marker:
200 value = self._traverse(self._weakmap, path, marker)
201 return value
202
203 def has(self, path, value=NOTHING, soft=False, hard=False, weak=False):
204 obj = self._getvalue(path, soft, hard, weak)
205 marker = NOTHING
206 if obj is marker:
207 return False
208 elif value is marker:
209 return True
210 result = self._backend.has(obj, value)
211 if result is NotImplemented:
212 raise PersistError("Can't check %r for containment" % type(obj))
213 return result
214
215 def keys(self, path, soft=False, hard=False, weak=False):
216 obj = self._getvalue(path, soft, hard, weak)
217 if obj is NOTHING:
218 return []
219 result = self._backend.keys(obj)
220 if result is NotImplemented:
221 raise PersistError("Can't return keys for %s" % type(obj))
222 return result
223
224 def get(self, path, default=None, soft=False, hard=False, weak=False):
225 value = self._getvalue(path, soft, hard, weak)
226 if value is NOTHING:
227 return default
228 return self._backend.copy(value)
229
230 def set(self, path, value, soft=False, weak=False):
231 assert path
232 if type(path) is str:
233 path = path_string_to_tuple(path)
234 if soft:
235 map = self._softmap
236 elif weak:
237 map = self._weakmap
238 else:
239 self.assert_writable()
240 self._modified = True
241 map = self._hardmap
242 self._traverse(map, path, setvalue=value)
243
244 def add(self, path, value, unique=False, soft=False, weak=False):
245 assert path
246 if type(path) is str:
247 path = path_string_to_tuple(path)
248 if soft:
249 map = self._softmap
250 elif weak:
251 map = self._weakmap
252 else:
253 self.assert_writable()
254 self._modified = True
255 map = self._hardmap
256 if unique:
257 current = self._traverse(map, path)
258 if type(current) is list and value in current:
259 return
260 path = path+(sys.maxsize,)
261 self._traverse(map, path, setvalue=value)
262
263 def remove(self, path, value=NOTHING, soft=False, weak=False):
264 assert path
265 if type(path) is str:
266 path = path_string_to_tuple(path)
267 if soft:
268 map = self._softmap
269 elif weak:
270 map = self._weakmap
271 else:
272 self.assert_writable()
273 self._modified = True
274 map = self._hardmap
275 marker = NOTHING
276 while path:
277 if value is marker:
278 obj = self._traverse(map, path[:-1])
279 elem = path[-1]
280 isvalue = False
281 else:
282 obj = self._traverse(map, path)
283 elem = value
284 isvalue = True
285 result = False
286 if obj is not marker:
287 result = self._backend.remove(obj, elem, isvalue)
288 if result is NotImplemented:
289 raise PersistError("Can't remove %r from %r" %
290 (elem, type(obj)))
291 if self._backend.empty(obj):
292 if value is not marker:
293 value = marker
294 else:
295 path = path[:-1]
296 else:
297 break
298 return result
299
300 def move(self, oldpath, newpath, soft=False, weak=False):
301 if not (soft or weak):
302 self.assert_writable()
303 if type(oldpath) is str:
304 oldpath = path_string_to_tuple(oldpath)
305 if type(newpath) is str:
306 newpath = path_string_to_tuple(newpath)
307 result = False
308 marker = NOTHING
309 value = self._getvalue(oldpath, soft, not (soft or weak), weak)
310 if value is not marker:
311 self.remove(oldpath, soft=soft, weak=weak)
312 self.set(newpath, value, weak, soft)
313 result = True
314 return result
315
316 def root_at(self, path):
317 return RootedPersist(self, path)
318
319
320class RootedPersist:
321
322 def __init__(self, parent, root):
323 self.parent = parent
324 if type(root) is str:
325 self.root = path_string_to_tuple(root)
326 else:
327 self.root = root
328
329 readonly = property(lambda self: self.parent.readonly)
330 modified = property(lambda self: self.parent.modified)
331
332 def assert_writable(self):
333 self.parent.assert_writable()
334
335 def save(self):
336 self.parent.save()
337
338 def has(self, path, value=NOTHING, soft=False, hard=False, weak=False):
339 if type(path) is str:
340 path = path_string_to_tuple(path)
341 return self.parent.has(self.root+path, value, soft, hard, weak)
342
343 def keys(self, path, soft=False, hard=False, weak=False):
344 if type(path) is str:
345 path = path_string_to_tuple(path)
346 return self.parent.keys(self.root+path, soft, hard, weak)
347
348 def get(self, path, default=None, soft=False, hard=False, weak=False):
349 if type(path) is str:
350 path = path_string_to_tuple(path)
351 return self.parent.get(self.root+path, default, soft, hard, weak)
352
353 def set(self, path, value, soft=False, weak=False):
354 if type(path) is str:
355 path = path_string_to_tuple(path)
356 return self.parent.set(self.root+path, value, soft, weak)
357
358 def add(self, path, value, unique=False, soft=False, weak=False):
359 if type(path) is str:
360 path = path_string_to_tuple(path)
361 return self.parent.add(self.root+path, value, unique, soft, weak)
362
363 def remove(self, path, value=NOTHING, soft=False, weak=False):
364 if type(path) is str:
365 path = path_string_to_tuple(path)
366 return self.parent.remove(self.root+path, value, soft, weak)
367
368 def move(self, oldpath, newpath, soft=False, weak=False):
369 if type(oldpath) is str:
370 oldpath = path_string_to_tuple(oldpath)
371 if type(newpath) is str:
372 newpath = path_string_to_tuple(newpath)
373 return self.parent.move(self.root+oldpath, self.root+newpath,
374 soft, weak)
375
376 def root_at(self, path):
377 if type(path) is str:
378 path = path_string_to_tuple(path)
379 return self.parent.root_at(self.root+path)
380
381
382_splitpath = re.compile(r"(\[-?\d+\])|(?<!\\)\.").split
383
384def path_string_to_tuple(path):
385 if "." not in path and "[" not in path:
386 return (path,)
387 result = []
388 tokens = _splitpath(path)
389 for token in tokens:
390 if token:
391 if token[0] == "[" and token[-1] == "]":
392 try:
393 result.append(int(token[1:-1]))
394 except ValueError:
395 raise PersistError("Invalid path index: %r" % token)
396 else:
397 result.append(token.replace(r"\.", "."))
398 return tuple(result)
399
400
401def path_tuple_to_string(path):
402 result = []
403 for elem in path:
404 if type(elem) is int:
405 result[-1] += "[%d]" % elem
406 else:
407 result.append(str(elem).replace(".", "\."))
408 return ".".join(result)
409
410
411class Backend:
412
413 def new(self):
414 return {}
415
416 def load(self, filepath):
417 raise NotImplementedError
418
419 def save(self, filepath, map):
420 raise NotImplementedError
421
422 def get(self, obj, elem, _marker=NOTHING):
423 if type(obj) is dict:
424 newobj = obj.get(elem, _marker)
425 elif type(obj) in (tuple, list):
426 if type(elem) is int:
427 try:
428 newobj = obj[elem]
429 except IndexError:
430 newobj = _marker
431 elif elem in obj:
432 newobj = elem
433 else:
434 newobj = _marker
435 else:
436 newobj = NotImplemented
437 return newobj
438
439 def set(self, obj, elem, value):
440 if type(obj) is dict:
441 newobj = obj[elem] = value
442 elif type(obj) is list and type(elem) is int:
443 lenobj = len(obj)
444 if lenobj <= elem:
445 obj.append(None)
446 elem = lenobj
447 elif elem < 0 and abs(elem) > lenobj:
448 obj.insert(0, None)
449 elem = 0
450 newobj = obj[elem] = value
451 else:
452 newobj = NotImplemented
453 return newobj
454
455 def remove(self, obj, elem, isvalue):
456 result = False
457 if type(obj) is dict:
458 if elem in obj:
459 del obj[elem]
460 result = True
461 elif type(obj) is list:
462 if not isvalue and type(elem) is int:
463 try:
464 del obj[elem]
465 result = True
466 except IndexError:
467 pass
468 elif elem in obj:
469 obj[:] = [x for x in obj if x != elem]
470 result = True
471 else:
472 result = NotImplemented
473 return result
474
475 def copy(self, value):
476 if type(value) in (dict, list):
477 return copy.deepcopy(value)
478 return value
479
480 def empty(self, obj):
481 return (not obj)
482
483 def has(self, obj, elem):
484 contains = getattr(obj, "__contains__", None)
485 if contains:
486 return contains(elem)
487 return NotImplemented
488
489 def keys(self, obj):
490 keys = getattr(obj, "keys", None)
491 if keys:
492 return keys()
493 elif type(obj) is list:
494 return list(range(len(obj)))
495 return NotImplemented
496
497
498class MemoryBackend(Backend):
499
500 def __init__(self):
501 self._store = {}
502
503 def load(self, filepath):
504 return self._store.get(filepath)
505
506 def save(self, filepath, map):
507 self._store[filepath] = map
508
509class DiskBackend(Backend):
510
511 safe_file_closing = True
512
513class PickleBackend(DiskBackend):
514
515 def __init__(self):
516 import pickle
517 self._pickle = cPickle
518
519 def load(self, filepath):
520 file = open(filepath, "rb")
521 try:
522 return self._pickle.load(file)
523 finally:
524 safe_close(file, self.safe_file_closing)
525
526 def save(self, filepath, map):
527 file = open(filepath, "wb")
528 try:
529 self._pickle.dump(map, file, 2)
530 finally:
531 safe_close(file, self.safe_file_closing)
532
533
534class BPickleBackend(DiskBackend):
535
536 def __init__(self):
537 from checkbox.contrib import bpickle
538 self._bpickle = bpickle
539
540 def load(self, filepath):
541 file = open(filepath, "rb")
542 try:
543 return self._bpickle.loads(file.read())
544 finally:
545 safe_close(file, self.safe_file_closing)
546
547 def save(self, filepath, map):
548 file = open(filepath, "wb")
549 try:
550 file.write(self._bpickle.dumps(map))
551 finally:
552 safe_close(file, self.safe_file_closing)
0553
=== added file 'checkbox/contrib/xrandr.py'
--- checkbox/contrib/xrandr.py 1970-01-01 00:00:00 +0000
+++ checkbox/contrib/xrandr.py 2013-10-01 18:38:21 +0000
@@ -0,0 +1,1064 @@
1#!/usr/bin/python3
2# -*- coding: utf-8 -*-
3#
4# Python-XRandR provides a high level API for the XRandR extension of the
5# X.org server. XRandR allows to configure resolution, refresh rate, rotation
6# of the screen and multiple outputs of graphics cards.
7#
8# Copyright 2007 © Sebastian Heinlein <sebastian.heinlein@web.de>
9# Copyright 2007 © Michael Vogt <mvo@ubuntu.com>
10# Copyright 2007 © Canonical Ltd.
11#
12# In many aspects it follows the design of the xrand tool of the X.org, which
13# comes with the following copyright:
14#
15# Copyright © 2001 Keith Packard, member of The XFree86 Project, Inc.
16# Copyright © 2002 Hewlett Packard Company, Inc.
17# Copyright © 2006 Intel Corporation
18#
19# And can be downloaded here:
20#
21# git://anongit.freedesktop.org/git/xorg/app/xrandr
22#
23# This library is free software; you can redistribute it and/or
24# modify it under the terms of the GNU Lesser General Public
25# License as published by the Free Software Foundation; either
26# version 2.1 of the License, or any later version.
27#
28# This library is distributed in the hope that it will be useful,
29# but WITHOUT ANY WARRANTY; without even the implied warranty of
30# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
31# Lesser General Public License for more details.
32#
33# You should have received a copy of the GNU Lesser General Public
34# License along with this library; if not, write to the Free Software
35# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
36# MA 02110-1301 USA
37
38from ctypes import (
39 POINTER,
40 Structure,
41 byref,
42 c_char_p,
43 c_void_p,
44 c_int,
45 c_long,
46 c_ulong,
47 c_ushort,
48 cdll,
49)
50import os
51
52RR_ROTATE_0 = 1
53RR_ROTATE_90 = 2
54RR_ROTATE_180 = 4
55RR_ROTATE_270 = 8
56RR_REFLECT_X = 16
57RR_REFLECT_Y = 32
58
59RR_CONNECTED = 0
60RR_DISCONNECTED = 1
61RR_UNKOWN_CONNECTION = 2
62
63RR_BAD_OUTPUT = 0
64RR_BAD_CRTC = 1
65RR_BAD_MODE = 2
66
67RR_SET_CONFIG_SUCCESS = 0
68RR_SET_CONFIG_INVALID_CONFIG_TIME = 1
69RR_SET_CONFIG_INVALID_TIME = 2
70RR_SET_CONFIG_FAILED = 3
71
72# Flags to keep track of changes
73CHANGES_NONE = 0
74CHANGES_CRTC = 1
75CHANGES_MODE = 2
76CHANGES_RELATION = 4
77CHANGES_POSITION = 8
78CHANGES_ROTATION = 16
79CHANGES_REFLECTION = 32
80CHANGES_AUTOMATIC = 64
81CHANGES_REFRESH = 128
82CHANGES_PROPERTY = 256
83
84# Relation information
85RELATION_ABOVE = 0
86RELATION_BELOW = 1
87RELATION_RIGHT_OF = 2
88RELATION_LEFT_OF = 3
89RELATION_SAME_AS = 4
90
91# some fundamental datatypes
92RRCrtc = c_long
93RROutput = c_long
94RRMode = c_long
95Connection = c_ushort
96SubpixelOrder = c_ushort
97Time = c_ulong
98Rotation = c_ushort
99Status = c_int
100
101# load the libs
102xlib = cdll.LoadLibrary('libX11.so.6')
103rr = cdll.LoadLibrary('libXrandr.so.2')
104
105
106# query resources
107class _XRRModeInfo(Structure):
108 _fields_ = [
109 ("id", RRMode), # XID is c_long
110 ("width", c_int),
111 ("height", c_int),
112 ("dotClock", c_long),
113 ("hSyncStart", c_int),
114 ("hSyncEnd", c_int),
115 ("hTotal", c_int),
116 ("hSkew", c_int),
117 ("vSyncStart", c_int),
118 ("vSyncEnd", c_int),
119 ("vTotal", c_int),
120 ("name", c_char_p),
121 ("nameLength", c_int),
122 ("modeFlags", c_long),
123 ]
124
125
126class _XRRScreenSize(Structure):
127 _fields_ = [
128 ("width", c_int),
129 ("height", c_int),
130 ("mwidth", c_int),
131 ("mheight", c_int)
132 ]
133
134
135class _XRRCrtcInfo(Structure):
136 _fields_ = [
137 ("timestamp", Time),
138 ("x", c_int),
139 ("y", c_int),
140 ("width", c_int),
141 ("height", c_int),
142 ("mode", RRMode),
143 ("rotation", c_int),
144 ("noutput", c_int),
145 ("outputs", POINTER(RROutput)),
146 ("rotations", Rotation),
147 ("npossible", c_int),
148 ("possible", POINTER(RROutput)),
149 ]
150
151
152class _XRRScreenResources(Structure):
153 _fields_ = [
154 ("timestamp", Time),
155 ("configTimestamp", Time),
156 ("ncrtc", c_int),
157 ("crtcs", POINTER(RRCrtc)),
158 ("noutput", c_int),
159 ("outputs", POINTER(RROutput)),
160 ("nmode", c_int),
161 ("modes", POINTER(_XRRModeInfo)),
162 ]
163
164
165class RRError(Exception):
166 """Base exception class of the module"""
167 pass
168
169
170class UnsupportedRRError(RRError):
171 """Raised if the required XRandR extension version is not available"""
172 def __init__(self, required, current):
173 self.required = required
174 self.current = current
175
176
177# XRRGetOutputInfo
178class _XRROutputInfo(Structure):
179 _fields_ = [
180 ("timestamp", Time),
181 ("crtc", c_int),
182 ("name", c_char_p),
183 ("nameLen", c_int),
184 ("mm_width", c_ulong),
185 ("mm_height", c_ulong),
186 ("connection", Connection),
187 ("subpixel_order", SubpixelOrder),
188 ("ncrtc", c_int),
189 ("crtcs", POINTER(RRCrtc)),
190 ("nclone", c_int),
191 ("clones", POINTER(RROutput)),
192 ("nmode", c_int),
193 ("npreferred", c_int),
194 ("modes", POINTER(RRMode))
195 ]
196
197
198class _XRRCrtcGamma(Structure):
199 _fields_ = [
200 ('size', c_int),
201 ('red', POINTER(c_ushort)),
202 ('green', POINTER(c_ushort)),
203 ('blue', POINTER(c_ushort)),
204 ]
205
206
207def _array_conv(array, type, conv=lambda x: x):
208 length = len(array)
209 res = (type * length)()
210 for i in range(length):
211 res[i] = conv(array[i])
212 return res
213
214
215class Output:
216 """The output is a reference to a supported output jacket of the graphics
217 card. Outputs are attached to a hardware pipe to be used. Furthermore
218 they can be a clone of another output or show a subset of the screen"""
219 def __init__(self, info, id, screen):
220 """Initializes an output instance"""
221 self._info = info
222 self.id = id
223 self._screen = screen
224 # Store changes later here
225 self._mode = None
226 self._crtc = None
227 self._rotation = RR_ROTATE_0
228 self._relation = None
229 self._relation_offset = 0
230 self._relative_to = None
231 self._position = None
232 self._reflection = None
233 self._automatic = None
234 self._rate = None
235 self._changes = CHANGES_NONE
236 self._x = 0
237 self._y = 0
238
239 self.name = self._info.contents.name
240
241 def __del__(self):
242 """Frees the internal reference to the output info if the output gets
243 removed"""
244 rr.XRRFreeOutputInfo(self._info)
245
246 def get_physical_width(self):
247 """Returns the display width reported by the connected output device"""
248 return self._info.contents.mm_width
249
250 def get_physical_height(self):
251 """
252 Returns the display height reported by the connected output device
253 """
254 return self._info.contents.mm_height
255
256 def get_crtc(self):
257 """Returns the xid of the hardware pipe to which the output is
258 attached. If the output is disabled it will return 0"""
259 return self._info.contents.crtc
260
261 def get_crtcs(self):
262 """Returns the xids of the hardware pipes to which the output could
263 be attached"""
264 crtcs = []
265 for i in range(self._info.contents.ncrtc):
266 for crtc in self._screen.crtcs:
267 if crtc.xid == self._info.contents.crtcs[i]:
268 crtcs.append(crtc)
269 return crtcs
270
271 def get_available_rotations(self):
272 """Returns a binary flag of the supported rotations of the output or
273 0 if the output is disabled"""
274 rotations = RR_ROTATE_0
275 found = False
276 if self.is_active():
277 # Get the rotations supported by all crtcs to make assigning
278 # crtcs easier. Furthermore there don't seem to be so many
279 # cards which show another behavior
280 for crtc in self.get_crtcs():
281 # Set rotations to the value of the first found crtc and
282 # then create a subset only for all other crtcs
283 if not found:
284 rotations = crtc.get_available_rotations()
285 found = True
286 else:
287 rotations = rotations & crtc.get_available_rotations()
288 return rotations
289
290 def get_available_modes(self):
291 """Returns the list of supported mode lines (resolution, refresh rate)
292 that are supported by the connected device"""
293 modes = []
294 for m in range(self._info.contents.nmode):
295 output_modes = self._info.contents.modes
296 for s in range(self._screen._resources.contents.nmode):
297 screen_modes = self._screen._resources.contents.modes
298 if screen_modes[s].id == output_modes[m]:
299 modes.append(screen_modes[s])
300 return modes
301
302 def get_preferred_mode(self):
303 """Returns an index that refers to the list of available modes and
304 points to the preferred mode of the connected device"""
305 return self._info.contents.npreferred
306
307 def is_active(self):
308 """Returns True if the output is attached to a hardware pipe, is
309 enabled"""
310 return self._info.contents.crtc != 0
311
312 def is_connected(self):
313 """Return True if a device is detected at the output"""
314 if self._info.contents.connection in (RR_CONNECTED,
315 RR_UNKOWN_CONNECTION):
316 return True
317 return False
318
319 def disable(self):
320 """Disables the output"""
321 if not self.is_active():
322 return
323 self._mode = None
324 self._crtc._outputs.remove(self)
325 self._crtc = None
326 self._changes = self._changes | CHANGES_CRTC | CHANGES_MODE
327
328 def set_to_mode(self, mode):
329 modes = self.get_available_modes()
330 if mode in range(len(modes)):
331 self._mode = modes[mode].id
332 return
333 raise RRError("Mode is not available")
334
335 def set_to_preferred_mode(self):
336 modes = self.get_available_modes()
337 mode = modes[self.get_preferred_mode()]
338 if mode != None:
339 self._mode = mode.id
340 return
341 raise RRError("Preferred mode is not available")
342
343 def get_clones(self):
344 """Returns the xids of the outputs which can be clones of the output"""
345 clones = []
346 for i in range(self._info.contents.nclone):
347 id = self._info.contents.clones[i]
348 o = self._screen.get_output_by_id(id)
349 clones.append(o)
350 return clones
351
352 def set_relation(self, relative, relation, offset=0):
353 """Sets the position of the output in relation to the given one"""
354 rel = self._screen.get_output_by_name(relative)
355 if rel and relation in (RELATION_LEFT_OF, RELATION_RIGHT_OF,
356 RELATION_ABOVE, RELATION_BELOW,
357 RELATION_SAME_AS):
358 self._relation = relation
359 self._relative_to = rel
360 self._relation_offset = offset
361 self._changes = self._changes | CHANGES_RELATION
362 else:
363 raise RRError("The given relative output or relation is not "
364 "available")
365
366 def has_changed(self, changes=None):
367 """Checks if the output has changed: Either for a specific change or
368 generally"""
369 if changes:
370 return self._changes & changes
371 else:
372 return self._changes != CHANGES_NONE
373
374
375class Crtc:
376 """The crtc is a reference to a hardware pipe that is provided by the
377 graphics device. Outputs can be attached to crtcs"""
378
379 def __init__(self, info, xid, screen):
380 """Initializes the hardware pipe object"""
381 self._info = info
382 self.xid = xid
383 self._screen = screen
384 self._outputs = []
385
386 def __del__(self):
387 """Frees the reference to the rendering pipe if the instance gets
388 removed"""
389 rr.XRRFreeCrtcConfigInfo(self._info)
390
391 def get_xid(self):
392 """Returns the internal id of the crtc from the X server"""
393 return self.xid
394
395 def get_available_rotations(self):
396 """Returns a binary flag that contains the supported rotations of the
397 hardware pipe"""
398 return self._info.contents.rotations
399
400 def set_config(self, x, y, mode, outputs, rotation=RR_ROTATE_0):
401 """Configures the render pipe with the given mode and outputs. X and y
402 set the position of the crtc output in the screen"""
403 rr.XRRSetCrtcConfig(self._screen._display,
404 self._screen._resources,
405 self.xid,
406 self._screen.get_timestamp(),
407 c_int(x), c_int(y),
408 mode,
409 rotation,
410 _array_conv(outputs, RROutput, lambda x: x.id),
411 len(outputs))
412
413 def apply_changes(self):
414 """Applies the stored changes"""
415 if len(self._outputs) > 0:
416 output = self._outputs[0]
417 self.set_config(output._x, output._y, output._mode,
418 self._outputs, output._rotation)
419 else:
420 self.disable()
421
422 def disable(self):
423 """Turns off all outputs on the crtc"""
424 rr.XRRSetCrtcConfig(self._screen._display,
425 self._screen._resources,
426 self.xid,
427 self._screen.get_timestamp(),
428 0, 0,
429 None,
430 RR_ROTATE_0,
431 0, 0)
432
433 #FIXME: support gamma settings
434 """
435 def get_gamma_size(self):
436 return rr.XRRGetCrtcGammaSize(self._screen._display, self.id)
437 def get_gamma(self):
438 result = rr.XRRGetCrtcGamma(self._screen._display, self.id)
439 return _from_gamma(result)
440 def set_gamma(self, gamma):
441 g = _to_gamma(gamma)
442 rr.XRRSetCrtcGamma(self._screen._display, self.id, g)
443 rr.XRRFreeGamma(g)
444 gamma = property(get_gamma, set_gamma)"""
445
446 def load_outputs(self):
447 """Get the currently assigned outputs"""
448 outputs = []
449 for i in range(self._info.contents.noutput):
450 id = self._info.contents.outputs[i]
451 o = self._screen.get_output_by_id(id)
452 outputs.append(o)
453 self._outputs = outputs
454
455 def get_outputs(self):
456 """Returns the list of attached outputs"""
457 return self._outputs
458
459 def add_output(self, output):
460 """Adds the specified output to the crtc"""
461 output._crtc = self
462 self._outputs.append(output)
463
464 def supports_output(self, output):
465 """Check if the output can be used by the crtc.
466 See check_crtc_for_output in xrandr.c"""
467 if not self.xid in [c.xid for c in output.get_crtcs()]:
468 return False
469 if len(self._outputs):
470 for other in self._outputs:
471 if other == output:
472 continue
473 if other._x != output._x:
474 return False
475 if other._y != output._y:
476 return False
477 if other._mode != output._mode:
478 return False
479 if other._rotation != output._rotation:
480 return False
481 #FIXME: pick_crtc is still missing
482 elif self._info.contents.noutput > 0:
483 if self._info.contents.x != output._x:
484 return False
485 if self._info.contents.y != output._y:
486 return False
487 if self._info.contents.mode_info != output._mode:
488 return False
489 if self._info.rotation != output._rotation:
490 return False
491 return True
492
493 def supports_rotation(self, rotation):
494 """Check if the given rotation is supported by the crtc"""
495 rotations = self._info.contents.rotations
496 dir = rotation & (RR_ROTATE_0 | RR_ROTATE_90 | RR_ROTATE_180 |
497 RR_ROTATE_270)
498 reflect = rotation & (RR_REFLECT_X | RR_REFLECT_Y)
499 if (((rotations & dir) != 0) and ((rotations & reflect) == reflect)):
500 return True
501 return False
502
503 def has_changed(self):
504 """Check if there are any new outputs assigned to the crtc or any
505 outputs with a changed mode or position"""
506 if len(self._outputs) != self._info.contents.noutput:
507 return True
508 for i in range(self._info.contents.noutput):
509 id = self._info.contents.outputs[i]
510 output = self._screen.get_output_by_id(id)
511 if not output in self._outputs:
512 return True
513 if output.has_changed():
514 return True
515 return False
516
517
518class Screen:
519 def __init__(self, dpy, screen=-1):
520 """Initializes the screen"""
521 # Some sane default values
522 self.outputs = {}
523 self.crtcs = []
524 self._width = 0
525 self._height = 0
526 self._width_max = 0
527 self._height_max = 0
528 self._width_min = 0
529 self._height_min = 0
530 self._width_mm = 0
531 self._height_mm = 0
532
533 self._display = dpy
534 if not -1 <= screen < xlib.XScreenCount(dpy):
535 raise RRError("The chosen screen is not available", screen)
536 elif screen == -1:
537 self._screen = xlib.XDefaultScreen(dpy)
538 else:
539 self._screen = screen
540 self._root = xlib.XDefaultRootWindow(self._display, self._screen)
541 self._id = rr.XRRRootToScreen(self._display, self._root)
542
543 self._load_resources()
544 self._load_config()
545 (self._width, self._height,
546 self._width_mm, self._height_mm) = self.get_size()
547 if XRANDR_VERSION >= (1, 2):
548 self._load_screen_size_range()
549 self._load_crtcs()
550 self._load_outputs()
551
552 # Store XRandR 1.0 changes here
553 self._rate = self.get_current_rate()
554 self._rotation = self.get_current_rotation()
555 self._size_index = self.get_current_size_index()
556
557 def __del__(self):
558 """Free the reference to the interal screen config if the screen
559 gets removed"""
560 rr.XRRFreeScreenConfigInfo(self._config)
561
562 def _load_config(self):
563 """Loads the screen configuration. Only needed privately by the
564 the bindings"""
565 class XRRScreenConfiguration(Structure):
566 " private to Xrandr "
567 pass
568 gsi = rr.XRRGetScreenInfo
569 gsi.restype = POINTER(XRRScreenConfiguration)
570 self._config = gsi(self._display, self._root)
571
572 def _load_screen_size_range(self):
573 """Detects the dimensionios of the screen"""
574 minWidth = c_int()
575 minHeight = c_int()
576 maxWidth = c_int()
577 maxHeight = c_int()
578 res = rr.XRRGetScreenSizeRange(self._display, self._root,
579 byref(minWidth), byref(minHeight),
580 byref(maxWidth), byref(maxHeight))
581 if res:
582 self._width_max = maxWidth.value
583 self._width_min = minWidth.value
584 self._height_max = maxHeight.value
585 self._height_min = minHeight.value
586
587 def _load_resources(self):
588 """Loads the screen resources. Only needed privately for the
589 bindings"""
590 gsr = rr.XRRGetScreenResources
591 gsr.restype = POINTER(_XRRScreenResources)
592 self._resources = gsr(self._display, self._root)
593
594 def _load_crtcs(self):
595 """Loads the available XRandR 1.2 crtcs (hardware pipes) of
596 the screen"""
597 gci = rr.XRRGetCrtcInfo
598 gci.restype = POINTER(_XRRCrtcInfo)
599 c = self._resources.contents.crtcs
600 for i in range(self._resources.contents.ncrtc):
601 xrrcrtcinfo = gci(self._display, self._resources, c[i])
602 self.crtcs.append(Crtc(xrrcrtcinfo, c[i], self))
603
604 def _load_outputs(self):
605 """Loads the available XRandR 1.2 outputs of the screen"""
606 goi = rr.XRRGetOutputInfo
607 goi.restype = POINTER(_XRROutputInfo)
608 o = self._resources.contents.outputs
609 for i in range(self._resources.contents.noutput):
610 xrroutputinfo = goi(self._display, self._resources, o[i])
611 output = Output(xrroutputinfo, o[i], self)
612 self.outputs[xrroutputinfo.contents.name] = output
613 # Store the mode of the crtc in the output instance
614 crtc = self.get_crtc_by_xid(output.get_crtc())
615 if crtc:
616 output._mode = crtc._info.contents.mode
617 crtc.add_output(output)
618
619 def get_size(self):
620 """Returns the current pixel and physical size of the screen"""
621 width = xlib.XDisplayWidth(self._display, self._screen)
622 width_mm = xlib.XDisplayWidthMM(self._display, self._screen)
623 height = xlib.XDisplayHeight(self._display, self._screen)
624 height_mm = xlib.XDisplayHeightMM(self._display, self._screen)
625 return width, height, width_mm, height_mm
626
627 def get_timestamp(self):
628 """Creates a X timestamp that must be used when applying changes, since
629 they can be delayed"""
630 config_timestamp = Time()
631 rr.XRRTimes.restpye = c_ulong
632 return rr.XRRTimes(self._display, self._id, byref(config_timestamp))
633
634 def get_crtc_by_xid(self, xid):
635 """Returns the crtc with the given xid or None"""
636 for crtc in self.crtcs:
637 if crtc.xid == xid:
638 return crtc
639 return None
640
641 def get_current_rate(self):
642 """Returns the currently used refresh rate"""
643 _check_required_version((1, 0))
644 xccr = rr.XRRConfigCurrentRate
645 xccr.restype = c_int
646 return xccr(self._config)
647
648 def get_available_rates_for_size_index(self, size_index):
649 """Returns the refresh rates that are supported by the screen for
650 the given resolution. See get_available_sizes for the resolution to
651 which size_index points"""
652 _check_required_version((1, 0))
653 rates = []
654 nrates = c_int()
655 rr.XRRConfigRates.restype = POINTER(c_ushort)
656 _rates = rr.XRRConfigRates(self._config, size_index, byref(nrates))
657 for r in range(nrates.value):
658 rates.append(_rates[r])
659 return rates
660
661 def get_current_rotation(self):
662 """Returns the currently used rotation. Can be RR_ROTATE_0,
663 RR_ROTATE_90, RR_ROTATE_180 or RR_ROTATE_270"""
664 _check_required_version((1, 0))
665 current = c_ushort()
666 rr.XRRConfigRotations(self._config, byref(current))
667 return current.value
668
669 def get_available_rotations(self):
670 """Returns a binary flag that holds the available resolutions"""
671 _check_required_version((1, 0))
672 current = c_ushort()
673 rotations = rr.XRRConfigRotations(self._config, byref(current))
674 return rotations
675
676 def get_current_size_index(self):
677 """Returns the position of the currently used resolution size in the
678 list of available resolutions. See get_available_sizes"""
679 _check_required_version((1, 0))
680 rotation = c_ushort()
681 size = rr.XRRConfigCurrentConfiguration(self._config,
682 byref(rotation))
683 return size
684
685 def get_available_sizes(self):
686 """Returns the available resolution sizes of the screen. The size
687 index points to the corresponding resolution of this list"""
688 _check_required_version((1, 0))
689 sizes = []
690 nsizes = c_int()
691 xcs = rr.XRRConfigSizes
692 xcs.restype = POINTER(_XRRScreenSize)
693 _sizes = xcs(self._config, byref(nsizes))
694 for r in range(nsizes.value):
695 sizes.append(_sizes[r])
696 return sizes
697
698 def set_config(self, size_index, rate, rotation):
699 """Configures the screen with the given resolution at the given size
700 index, rotation and refresh rate. To get in effect call
701 Screen.apply_config()"""
702 _check_required_version((1, 0))
703 self.set_size_index(size_index)
704 self.set_refresh_rate(rate)
705 self.set_rotation(rotation)
706
707 def set_size_index(self, index):
708 """Sets the reoslution of the screen. To get in effect call
709 Screen.apply_config()"""
710 if index in range(len(self.get_available_sizes())):
711 self._size_index = index
712 else:
713 raise RRError("There isn't any size associated "
714 "to the index %s" % index)
715
716 def set_rotation(self, rotation):
717 """Sets the rotation of the screen. To get in effect call
718 Screen.apply_config()"""
719 if self.get_available_rotations() & rotation:
720 self._rotation = rotation
721 else:
722 raise RRError("The chosen rotation is not supported")
723
724 def set_refresh_rate(self, rate):
725 """Sets the refresh rate of the screen. To get in effect call
726 Screen.apply_config()"""
727 if rate in self.get_available_rates_for_size_index(self._size_index):
728 self._rate = rate
729 else:
730 raise RRError("The chosen refresh rate %s is not "
731 "supported" % rate)
732
733 def get_mode_by_xid(self, xid):
734 """Returns the mode of the given xid"""
735 screen_modes = self._resources.contents.modes
736 for s in range(self._resources.contents.nmode):
737 if screen_modes[s].id == xid:
738 return screen_modes[s]
739 return None
740
741 def get_output_by_name(self, name):
742 """Returns the output of the screen with the given name or None"""
743 if name in self.outputs:
744 return self.outputs[name]
745 else:
746 return None
747
748 def get_output_by_id(self, id):
749 """Returns the output of the screen with the given xid or None"""
750 for o in list(self.outputs.values()):
751 if o.id == id:
752 return o
753 return None
754
755 def print_info(self, verbose=False):
756 """Prints some information about the detected screen and its outputs"""
757 _check_required_version((1, 0))
758 print("Screen %s: minimum %s x %s, current %s x %s, maximum %s x %s" %\
759 (self._screen,
760 self._width_min, self._height_min,
761 self._width, self._height,
762 self._width_max, self._height_max))
763 print(" %smm x %smm" % (self._width_mm, self._height_mm))
764 print("Crtcs: %s" % len(self.crtcs))
765 if verbose:
766 print("Modes (%s):" % self._resources.contents.nmode)
767 modes = self._resources.contents.modes
768 for i in range(self._resources.contents.nmode):
769 print(" %s - %sx%s" % (modes[i].name,
770 modes[i].width,
771 modes[i].height))
772 i = 0
773 print("Sizes @ Refresh Rates:")
774 for s in self.get_available_sizes():
775 print(" [%s] %s x %s @ %s" % (
776 i, s.width, s.height,
777 self.get_available_rates_for_size_index(i)))
778 i += 1
779 print("Rotations:")
780 rots = self.get_available_rotations()
781 if rots & RR_ROTATE_0:
782 print("normal")
783 if rots & RR_ROTATE_90:
784 print("right")
785 if rots & RR_ROTATE_180:
786 print("inverted")
787 if rots & RR_ROTATE_270:
788 print("left")
789 print("")
790 print("Outputs:")
791 for o in list(self.outputs.keys()):
792 output = self.outputs[o]
793 print(" %s" % o)
794 if output.is_connected():
795 print("(%smm x %smm)" % (output.get_physical_width(),
796 output.get_physical_height()))
797 modes = output.get_available_modes()
798 print(" Modes:")
799 for m in range(len(modes)):
800 mode = modes[m]
801 refresh = mode.dotClock / (mode.hTotal * mode.vTotal)
802 print(" [%s] %s x %s @ %s" % (m,
803 mode.width,
804 mode.height,
805 refresh))
806 if mode.id == output._mode:
807 print("*")
808 if m == output.get_preferred_mode():
809 print("(preferred)")
810 print("")
811 print(" Rotations:")
812 rots = output.get_available_rotations()
813 if rots & RR_ROTATE_0:
814 print("normal")
815 if rots & RR_ROTATE_90:
816 print("right")
817 if rots & RR_ROTATE_180:
818 print("inverted")
819 if rots & RR_ROTATE_270:
820 print("left")
821 print("")
822 else:
823 print("(not connected)")
824 if verbose:
825 print(" Core properties:")
826 for (f, t) in output._info.contents._fields_:
827 print(" %s: %s" % (
828 f, getattr(output._info.contents, f)))
829
830 def get_outputs(self):
831 """Returns the outputs of the screen"""
832 _check_required_version((1, 2))
833 return list(self.outputs.values())
834
835 def get_output_names(self):
836 _check_required_version((1, 2))
837 return list(self.outputs.keys())
838
839 def set_size(self, width, height, width_mm, height_mm):
840 """Apply the given pixel and physical size to the screen"""
841 _check_required_version((1, 2))
842 # Check if we really need to apply the changes
843 if (width, height, width_mm, height_mm) == self.get_size():
844 return
845 rr.XRRSetScreenSize(self._display, self._root,
846 c_int(width), c_int(height),
847 c_int(width_mm), c_int(height_mm))
848
849 def apply_output_config(self):
850 """Used for instantly applying RandR 1.2 changes"""
851 _check_required_version((1, 2))
852 self._arrange_outputs()
853 self._calculate_size()
854 self.set_size(self._width, self._height,
855 self._width_mm, self._height_mm)
856
857 # Assign all active outputs to crtcs
858 for output in list(self.outputs.values()):
859 if not output._mode or output._crtc:
860 continue
861 for crtc in output.get_crtcs():
862 if crtc and crtc.supports_output(output):
863 crtc.add_output(output)
864 output._changes = output._changes | CHANGES_CRTC
865 break
866 if not output._crtc:
867 #FIXME: Take a look at the pick_crtc code in xrandr.c
868 raise RRError("There is no matching crtc for the output")
869
870 # Apply stored changes of crtcs
871 for crtc in self.crtcs:
872 if crtc.has_changed():
873 crtc.apply_changes()
874
875 def apply_config(self):
876 """Used for instantly applying RandR 1.0 changes"""
877 _check_required_version((1, 0))
878 status = rr.XRRSetScreenConfigAndRate(self._display,
879 self._config,
880 self._root,
881 self._size_index,
882 self._rotation,
883 self._rate,
884 self.get_timestamp())
885 return status
886
887 def _arrange_outputs(self):
888 """Arrange all output positions according to their relative position"""
889 for output in self.get_outputs():
890 # Skip not changed and not used outputs
891 if not output.has_changed(CHANGES_RELATION) or \
892 output._mode == None:
893 continue
894 relative = output._relative_to
895 mode = self.get_mode_by_xid(output._mode)
896 mode_relative = self.get_mode_by_xid(relative._mode)
897 if not relative or not relative._mode:
898 output._x = 0
899 output._y = 0
900 output._changes = output._changes | CHANGES_POSITION
901 if output._relation == RELATION_LEFT_OF:
902 output._y = relative._y + output._relation_offset
903 output._x = relative._x - \
904 get_mode_width(mode, output._rotation)
905 elif output._relation == RELATION_RIGHT_OF:
906 output._y = relative._y + output._relation_offset
907 output._x = relative._x + get_mode_width(mode_relative,
908 output._rotation)
909 elif output._relation == RELATION_ABOVE:
910 output._y = relative._y - get_mode_height(mode,
911 output._rotation)
912 output._x = relative._x + output._relation_offset
913 elif output._relation == RELATION_BELOW:
914 output._y = relative._y + get_mode_height(mode_relative,
915 output._rotation)
916 output._x = relative._x + output._relation_offset
917 elif output._relation == RELATION_SAME_AS:
918 output._y = relative._y + output._relation_offset
919 output._x = relative._x + output._relation_offset
920 output._changes = output._changes | CHANGES_POSITION
921 # Normalize the postions so to the upper left cornor of all outputs
922 # is at 0,0
923 min_x = 32768
924 min_y = 32768
925 for output in self.get_outputs():
926 if output._mode == None:
927 continue
928 if output._x < min_x:
929 min_x = output._x
930 if output._y < min_y:
931 min_y = output._y
932 for output in self.get_outputs():
933 if output._mode == None:
934 continue
935 output._x -= min_x
936 output._y -= min_y
937 output._changes = output._changes | CHANGES_POSITION
938
939 def _calculate_size(self):
940 """Recalculate the pixel and physical size of the screen so that
941 it covers all outputs"""
942 width = self._width
943 height = self._height
944 for output in self.get_outputs():
945 if not output._mode:
946 continue
947 mode = self.get_mode_by_xid(output._mode)
948 x = output._x
949 y = output._y
950 w = get_mode_width(mode, output._rotation)
951 h = get_mode_height(mode, output._rotation)
952 if x + w > width:
953 width = x + w
954 if y + h > height:
955 height = y + h
956 if width > self._width_max or height > self._height_max:
957 raise RRError("The required size is not supported",
958 (width, height), (self._width_max, self._width_min))
959 else:
960 if height < self._height_min:
961 self._fb_height = self._height_min
962 else:
963 self._height = height
964 if width < self._width_min:
965 self._width = self._width_min
966 else:
967 self._width = width
968 #FIXME: Physical size is missing
969
970
971def get_current_display():
972 """Returns the currently used display"""
973 display_url = os.getenv("DISPLAY")
974 open_display = xlib.XOpenDisplay
975 # Set .argtypes and .restype, to ensure proper
976 # type check and conversion
977 open_display.restype = c_void_p
978 open_display.argtypes = [c_char_p]
979 # XOpenDisplay accepts a char*, but
980 # display_url is a unicode string therefore
981 # we convert it to a bytes string
982 dpy = open_display(display_url.encode('utf-8'))
983 return dpy
984
985
986def get_current_screen():
987 """Returns the currently used screen"""
988 screen = Screen(get_current_display())
989 return screen
990
991
992def get_screen_of_display(display, count):
993 """Returns the screen of the given display"""
994 dpy = xlib.XOpenDisplay(display)
995 return Screen(dpy, count)
996
997
998def get_version():
999 """Returns a tuple containing the major and minor version of the xrandr
1000 extension or None if the extension is not available"""
1001 major = c_int()
1002 minor = c_int()
1003 res = rr.XRRQueryVersion(get_current_display(),
1004 byref(major), byref(minor))
1005 if res:
1006 return (major.value, minor.value)
1007 return None
1008
1009
1010def has_extension():
1011 """Returns True if the xrandr extension is available"""
1012 if XRANDR_VERSION:
1013 return True
1014 return False
1015
1016
1017def _to_gamma(gamma):
1018 g = rr.XRRAllocGamma(len(gamma[0]))
1019 for i in range(gamma[0]):
1020 g.red[i] = gamma[0][i]
1021 g.green[i] = gamma[1][i]
1022 g.blue[i] = gamma[2][i]
1023 return g
1024
1025
1026def _from_gamma(g):
1027 gamma = ([], [], [])
1028 for i in range(g.size):
1029 gamma[0].append(g.red[i])
1030 gamma[1].append(g.green[i])
1031 gamma[2].append(g.blue[i])
1032 rr.XRRFreeGamma(g)
1033
1034
1035def _check_required_version(version):
1036 """Raises an exception if the given or a later version of xrandr is not
1037 available"""
1038 if XRANDR_VERSION == None or XRANDR_VERSION < version:
1039 raise UnsupportedRRError(version, XRANDR_VERSION)
1040
1041
1042def get_mode_height(mode, rotation):
1043 """Return the height of the given mode taking the rotation into account"""
1044 if rotation & (RR_ROTATE_0 | RR_ROTATE_180):
1045 return mode.height
1046 elif rotation & (RR_ROTATE_90 | RR_ROTATE_270):
1047 return mode.width
1048 else:
1049 return 0
1050
1051
1052def get_mode_width(mode, rotation):
1053 """Return the width of the given mode taking the rotation into account"""
1054 if rotation & (RR_ROTATE_0 | RR_ROTATE_180):
1055 return mode.width
1056 elif rotation & (RR_ROTATE_90 | RR_ROTATE_270):
1057 return mode.height
1058 else:
1059 return 0
1060
1061
1062XRANDR_VERSION = get_version()
1063
1064# vim:ts=4:sw=4:et
01065
=== added directory 'checkbox/dbus'
=== added file 'checkbox/dbus/__init__.py'
--- checkbox/dbus/__init__.py 1970-01-01 00:00:00 +0000
+++ checkbox/dbus/__init__.py 2013-10-01 18:38:21 +0000
@@ -0,0 +1,89 @@
1# This file is part of Checkbox.
2#
3# Copyright 2012 Canonical Ltd.
4# Written by:
5# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
6#
7# Checkbox is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# Checkbox is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
19#
20"""
21checkbox.dbus
22=============
23
24Utility modules for working with various things accessible over dbus
25"""
26
27import logging
28
29from dbus import SystemBus
30from dbus.mainloop.glib import DBusGMainLoop
31from dbus import (Array, Boolean, Byte, Dictionary, Double, Int16, Int32,
32 Int64, ObjectPath, String, Struct, UInt16, UInt32, UInt64)
33from gi.repository import GObject
34
35
36def connect_to_system_bus():
37 """
38 Connect to the system bus properly.
39
40 Returns a tuple (system_bus, loop) where loop is a GObject.MainLoop
41 instance. The loop is there so that you can listen to signals.
42 """
43 # We'll need an event loop to observe signals. We will need the instance
44 # later below so let's keep it. Note that we're not passing it directly
45 # below as DBus needs specific API. The DBusGMainLoop class that we
46 # instantiate and pass is going to work with this instance transparently.
47 #
48 # NOTE: DBus tutorial suggests that we should create the loop _before_
49 # connecting to the bus.
50 logging.debug("Setting up glib-based event loop")
51 loop = GObject.MainLoop()
52 # Let's get the system bus object. We need that to access UDisks2 object
53 logging.debug("Connecting to DBus system bus")
54 system_bus = SystemBus(mainloop=DBusGMainLoop())
55 return system_bus, loop
56
57
58def drop_dbus_type(value):
59 """
60 Convert types from the DBus bindings to their python counterparts.
61
62 This function is mostly lossless, except for arrays of bytes (DBus
63 signature "y") that are transparently converted to strings, assuming
64 an UTF-8 encoded string.
65
66 The point of this function is to simplify printing of nested DBus data that
67 gets displayed in a rather illegible way.
68 """
69 if isinstance(value, Array) and value.signature == "y":
70 # Some other things are reported as array of bytes that are just
71 # strings but due to Unix heritage the encoding is not known.
72 # In practice it is better to treat them as UTF-8 strings
73 return bytes(value).decode("UTF-8", "replace").strip("\0")
74 elif isinstance(value, (Struct, Array)):
75 return [drop_dbus_type(item) for item in value]
76 elif isinstance(value, (Dictionary)):
77 return {drop_dbus_type(dict_key): drop_dbus_type(dict_value)
78 for dict_key, dict_value in value.items()}
79 elif isinstance(value, (String, ObjectPath)):
80 return str(value)
81 elif isinstance(value, (Byte, UInt16, UInt32, UInt64,
82 Int16, Int32, Int64)):
83 return int(value)
84 elif isinstance(value, Boolean):
85 return bool(value)
86 elif isinstance(value, Double):
87 return float(value)
88 else:
89 return value
090
=== added file 'checkbox/dbus/udisks2.py'
--- checkbox/dbus/udisks2.py 1970-01-01 00:00:00 +0000
+++ checkbox/dbus/udisks2.py 2013-10-01 18:38:21 +0000
@@ -0,0 +1,479 @@
1# Copyright 2012 Canonical Ltd.
2# Written by:
3# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License version 3,
7# as published by the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17"""
18checkbox.dbus.udisks2
19=====================
20
21Module for working with UDisks2 from python.
22
23There are two main classes that are interesting here.
24
25The first class is UDisksObserver, which is easy to setup and has pythonic API
26to all of the stuff that happens in UDisks2. It offers simple signal handlers
27for any changes that occur in UDisks2 that were advertised by DBus.
28
29The second class is UDisksModel, that builds on the observer class to offer
30persistent collection of objects managed by UDisks2.
31
32To work with this model you will likely want to look at:
33 http://udisks.freedesktop.org/docs/latest/ref-dbus.html
34"""
35
36import logging
37
38from dbus import Interface, PROPERTIES_IFACE
39from dbus.exceptions import DBusException
40
41from checkbox.dbus import drop_dbus_type
42
43__all__ = ['UDisks2Observer', 'UDisks2Model', 'Signal', 'is_udisks2_supported',
44 'lookup_udev_device']
45
46
47def is_udisks2_supported(system_bus):
48 """
49 Check if udisks2 is available on the system bus.
50
51 ..note::
52 Calling this _may_ trigger activation of the UDisks2 daemon but it
53 should only happen on systems where it is already expected to run all
54 the time.
55 """
56 observer = UDisks2Observer()
57 try:
58 logging.debug("Trying to connect to UDisks2...")
59 observer.connect_to_bus(system_bus)
60 except DBusException as exc:
61 if exc.get_dbus_name() == "org.freedesktop.DBus.Error.ServiceUnknown":
62 logging.debug("No UDisks2 on the system bus")
63 return False
64 else:
65 raise
66 else:
67 logging.debug("Got UDisks2 connection")
68 return True
69
70
71def map_udisks1_connection_bus(udisks1_connection_bus):
72 """
73 Map the value of udisks1 ConnectionBus property to the corresponding values
74 in udisks2. This a lossy function as some values are no longer supported.
75
76 Incorrect values raise LookupError
77 """
78 return {
79 'ata_serial_esata': '', # gone from udisks2
80 'firewire': 'ieee1394', # renamed
81 'scsi': '', # gone from udisks2
82 'sdio': 'sdio', # as-is
83 'usb': 'usb', # as-is
84 }[udisks1_connection_bus]
85
86
87def lookup_udev_device(udisks2_object, udev_devices):
88 """
89 Find the udev_device that corresponds to the udisks2 object
90
91 Devices are matched by unix filesystem path of the special file (device).
92 The udisks2_object must implement the block device interface (so that the
93 block device path can be determined) or a ValueError is raised.
94
95 The udisks2_object must be the dictionary that maps from interface names to
96 dictionaries of properties. For compatible data see
97 UDisks2Model.managed_objects The udev_devices must be a list of udev
98 device, as returned from GUdev.
99
100 If there is no match, LookupError is raised with the unix block device
101 path.
102 """
103 try:
104 block_props = udisks2_object[UDISKS2_BLOCK_INTERFACE]
105 except KeyError:
106 raise ValueError("udisks2_object must be a block device")
107 else:
108 block_dev = block_props['Device']
109 for udev_device in udev_devices:
110 if udev_device.get_device_file() == block_dev:
111 return udev_device
112 raise LookupError(block_dev)
113
114
115# The well-known name for the ObjectManager interface, sadly it is not a part
116# of the python binding along with the rest of well-known names.
117OBJECT_MANAGER_INTERFACE = "org.freedesktop.DBus.ObjectManager"
118
119# The well-known name of the filesystem interface implemented by certain
120# objects exposed by UDisks2
121UDISKS2_FILESYSTEM_INTERFACE = "org.freedesktop.UDisks2.Filesystem"
122
123# The well-known name of the block (device) interface implemented by certain
124# objects exposed by UDisks2
125UDISKS2_BLOCK_INTERFACE = "org.freedesktop.UDisks2.Block"
126
127# The well-known name of the drive interface implemented by certain objects
128# exposed by UDisks2
129UDISKS2_DRIVE_INTERFACE = "org.freedesktop.UDisks2.Drive"
130
131
132class Signal:
133 """
134 Basic signal that supports arbitrary listeners.
135
136 While this class can be used directly it is best used with the helper
137 decorator Signal.define on a member function. The function body is ignored,
138 apart from the documentation.
139
140 The function name then becomes a unique (per encapsulating class instance)
141 object (an instance of this Signal class) that is created on demand.
142
143 In practice you just have a documentation and use
144 object.signal_name.connect() and object.signal_name(*args, **kwargs) to
145 fire it.
146 """
147
148 def __init__(self, signal_name):
149 """
150 Construct a signal with the given name
151 """
152 self._listeners = []
153 self._signal_name = signal_name
154
155 def connect(self, listener):
156 """
157 Connect a new listener to this signal
158
159 That listener will be called whenever fire() is invoked on the signal
160 """
161 self._listeners.append(listener)
162
163 def disconnect(self, listener):
164 """
165 Disconnect an existing listener from this signal
166 """
167 self._listeners.remove(listener)
168
169 def fire(self, args, kwargs):
170 """
171 Fire this signal with the specified arguments and keyword arguments.
172
173 Typically this is used by using __call__() on this object which is more
174 natural as it does all the argument packing/unpacking transparently.
175 """
176 for listener in self._listeners:
177 listener(*args, **kwargs)
178
179 def __call__(self, *args, **kwargs):
180 """
181 Call fire() with all arguments forwarded transparently
182 """
183 self.fire(args, kwargs)
184
185 @classmethod
186 def define(cls, dummy_func):
187 """
188 Helper decorator to define a signal descriptor in a class
189
190 The decorated function is never called but is used to get
191 documentation.
192 """
193 return _SignalDescriptor(dummy_func)
194
195
196class _SignalDescriptor:
197 """
198 Descriptor for convenient signal access.
199
200 Typically this class is used indirectly, when accessed from Signal.define
201 method decorator. It is used to do all the magic required when accessing
202 signal name on a class or instance.
203 """
204
205 def __init__(self, dummy_func):
206 self.signal_name = dummy_func.__name__
207 self.__doc__ = dummy_func.__doc__
208
209 def __repr__(self):
210 return "<SignalDecorator for signal: %r>" % self.signal_name
211
212 def __get__(self, instance, owner):
213 if instance is None:
214 return self
215 # Ensure that the instance has __signals__ property
216 if not hasattr(instance, "__signals__"):
217 instance.__signals__ = {}
218 if self.signal_name not in instance.__signals__:
219 instance.__signals__[self.signal_name] = Signal(self.signal_name)
220 return instance.__signals__[self.signal_name]
221
222 def __set__(self, instance, value):
223 raise AttributeError("You cannot overwrite signals")
224
225 def __delete__(self, instance):
226 raise AttributeError("You cannot delete signals")
227
228
229class UDisks2Observer:
230 """
231 Class for observing ongoing changes in UDisks2
232 """
233
234 def __init__(self):
235 """
236 Create a UDisks2 model.
237
238 The model must be connected to a bus before it is first used, see
239 connect()
240 """
241 # Proxy to the UDisks2 object
242 self._udisks2_obj = None
243 # Proxy to the ObjectManager interface exposed by UDisks2 object
244 self._udisks2_obj_manager = None
245
246 @Signal.define
247 def on_initial_objects(self, managed_objects):
248 """
249 Signal fired when the initial list of objects becomes available
250 """
251
252 @Signal.define
253 def on_interfaces_added(self, object_path, interfaces_and_properties):
254 """
255 Signal fired when one or more interfaces gets added to a specific
256 object.
257 """
258
259 @Signal.define
260 def on_interfaces_removed(self, object_path, interfaces):
261 """
262 Signal fired when one or more interface gets removed from a specific
263 object
264 """
265
266 @Signal.define
267 def on_properties_changed(self, interface_name, changed_properties,
268 invalidated_properties, sender=None):
269 """
270 Signal fired when one or more property changes value or becomes
271 invalidated.
272 """
273
274 def connect_to_bus(self, bus):
275 """
276 Establish initial connection to UDisks2 on the specified DBus bus.
277
278 This will also load the initial set of objects from UDisks2 and thus
279 fire the on_initial_objects() signal from the model. Please call this
280 method only after connecting that signal if you want to observe that
281 event.
282 """
283 # Once everything is ready connect to udisks2
284 self._connect_to_udisks2(bus)
285 # And read all the initial objects and setup
286 # change event handlers
287 self._get_initial_objects()
288
289 def _connect_to_udisks2(self, bus):
290 """
291 Setup the initial connection to UDisks2
292
293 This step can fail if UDisks2 is not available and cannot be
294 service-activated.
295 """
296 # Access the /org/freedesktop/UDisks2 object sitting on the
297 # org.freedesktop.UDisks2 bus name. This will trigger the necessary
298 # activation if udisksd is not running for any reason
299 logging.debug("Accessing main UDisks2 object")
300 self._udisks2_obj = bus.get_object(
301 "org.freedesktop.UDisks2", "/org/freedesktop/UDisks2")
302 # Now extract the standard ObjectManager interface so that we can
303 # observe and iterate the collection of objects that UDisks2 provides.
304 logging.debug("Accessing ObjectManager interface on UDisks2 object")
305 self._udisks2_obj_manager = Interface(
306 self._udisks2_obj, OBJECT_MANAGER_INTERFACE)
307 # Connect to the PropertiesChanged signal. Here unlike before we want
308 # to listen to all signals, regardless of who was sending them in the
309 # first place.
310 logging.debug("Setting up DBus signal handler for PropertiesChanged")
311 bus.add_signal_receiver(
312 self._on_properties_changed,
313 signal_name="PropertiesChanged",
314 dbus_interface=PROPERTIES_IFACE,
315 # Use the sender_keyword keyword argument to indicate that we wish
316 # to know the sender of each signal. For consistency with other
317 # signals we choose to use the 'object_path' keyword argument.
318 sender_keyword='sender')
319
320 def _get_initial_objects(self):
321 """
322 Get the initial collection of objects.
323
324 Needs to be called before the first signals from DBus are observed.
325 Requires a working connection to UDisks2.
326 """
327 # Having this interface we can now peek at the existing objects.
328 # We can use the standard method GetManagedObjects() to do that
329 logging.debug("Accessing GetManagedObjects() on UDisks2 object")
330 managed_objects = self._udisks2_obj_manager.GetManagedObjects()
331 managed_objects = drop_dbus_type(managed_objects)
332 # Fire the public signal for getting initial objects
333 self.on_initial_objects(managed_objects)
334 # Connect our internal handles to the DBus signal handlers
335 logging.debug("Setting up DBus signal handler for InterfacesAdded")
336 self._udisks2_obj_manager.connect_to_signal(
337 "InterfacesAdded", self._on_interfaces_added)
338 logging.debug("Setting up DBus signal handler for InterfacesRemoved")
339 self._udisks2_obj_manager.connect_to_signal(
340 "InterfacesRemoved", self._on_interfaces_removed)
341
342 def _on_interfaces_added(self, object_path, interfaces_and_properties):
343 """
344 Internal callback that is called by DBus
345
346 This function is responsible for firing the public signal
347 """
348 # Convert from dbus types
349 object_path = drop_dbus_type(object_path)
350 interfaces_and_properties = drop_dbus_type(interfaces_and_properties)
351 # Log what's going on
352 logging.debug("The object %r has gained the following interfaces and "
353 "properties: %r", object_path, interfaces_and_properties)
354 # Call the signal handler
355 self.on_interfaces_added(object_path, interfaces_and_properties)
356
357 def _on_interfaces_removed(self, object_path, interfaces):
358 """
359 Internal callback that is called by DBus
360
361 This function is responsible for firing the public signal
362 """
363 # Convert from dbus types
364 object_path = drop_dbus_type(object_path)
365 interfaces = drop_dbus_type(interfaces)
366 # Log what's going on
367 logging.debug("The object %r has lost the following interfaces: %r",
368 object_path, interfaces)
369 # Call the signal handler
370 self.on_interfaces_removed(object_path, interfaces)
371
372 def _on_properties_changed(self, interface_name, changed_properties,
373 invalidated_properties, sender=None):
374 """
375 Internal callback that is called by DBus
376
377 This function is responsible for firing the public signal
378 """
379 # Convert from dbus types
380 interface_name = drop_dbus_type(interface_name)
381 changed_properties = drop_dbus_type(changed_properties)
382 invalidated_properties = drop_dbus_type(invalidated_properties)
383 sender = drop_dbus_type(sender)
384 # Log what's going on
385 logging.debug("Some object with the interface %r has changed "
386 "properties: %r and invalidated properties %r "
387 "(sender: %s)",
388 interface_name, changed_properties,
389 invalidated_properties, sender)
390 # Call the signal handler
391 self.on_properties_changed(interface_name, changed_properties,
392 invalidated_properties, sender)
393
394
395class UDisks2Model:
396 """
397 Model for working with UDisks2
398
399 This class maintains a persistent model of what UDisks2 knows about, based
400 on the UDisks2Observer class and the signals it offers.
401 """
402
403 def __init__(self, observer):
404 """
405 Create a UDisks2 model.
406
407 The model will track changes using the specified observer (which is
408 expected to be a UDisks2Observer instance)
409
410 You should only connect the observer to the bus after creating the
411 model otherwise the initial objects will not be detected.
412 """
413 # Local state, everything that UDisks2 tells us
414 self._managed_objects = {}
415 self._observer = observer
416 # Connect all the signals to the observer
417 self._observer.on_initial_objects.connect(self._on_initial_objects)
418 self._observer.on_interfaces_added.connect(self._on_interfaces_added)
419 self._observer.on_interfaces_removed.connect(
420 self._on_interfaces_removed)
421 self._observer.on_properties_changed.connect(
422 self._on_properties_changed)
423
424 @Signal.define
425 def on_change(self):
426 """
427 Signal sent whenever the collection of managed object changes
428
429 Note that this signal is fired _after_ the change has occurred
430 """
431
432 @property
433 def managed_objects(self):
434 """
435 A collection of objects that is managed by this model. All changes as
436 well as the initial state, are reflected here.
437 """
438 return self._managed_objects
439
440 def _on_initial_objects(self, managed_objects):
441 """
442 Internal callback called when we get the initial collection of objects
443 """
444 self._managed_objects = drop_dbus_type(managed_objects)
445
446 def _on_interfaces_added(self, object_path, interfaces_and_properties):
447 """
448 Internal callback called when an interface is added to certain object
449 """
450 # Update internal state
451 if object_path not in self._managed_objects:
452 self._managed_objects[object_path] = {}
453 obj = self._managed_objects[object_path]
454 obj.update(interfaces_and_properties)
455 # Fire the change signal
456 self.on_change()
457
458 def _on_interfaces_removed(self, object_path, interfaces):
459 """
460 Internal callback called when an interface is removed from a certain
461 object
462 """
463 # Update internal state
464 if object_path in self._managed_objects:
465 obj = self._managed_objects[object_path]
466 for interface in interfaces:
467 if interface in obj:
468 del obj[interface]
469 # Fire the change signal
470 self.on_change()
471
472 def _on_properties_changed(self, interface_name, changed_properties,
473 invalidated_properties, sender=None):
474 # XXX: This is a workaround the fact that we cannot
475 # properly track changes to all properties :-(
476 self._managed_objects = drop_dbus_type(
477 self._observer._udisks2_obj_manager.GetManagedObjects())
478 # Fire the change signal()
479 self.on_change()
0480
=== added file 'checkbox/dispatcher.py'
--- checkbox/dispatcher.py 1970-01-01 00:00:00 +0000
+++ checkbox/dispatcher.py 2013-10-01 18:38:21 +0000
@@ -0,0 +1,216 @@
1#
2# This file is part of Checkbox.
3#
4# Copyright 2010-12 Canonical Ltd.
5#
6# Checkbox is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# Checkbox is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
18#
19__metaclass__ = type
20
21__all__ = [
22 "Dispatcher",
23 "DispatcherList",
24 "DispatcherQueue",
25 ]
26
27import logging
28
29from itertools import product
30
31
32class Event:
33 """Event payload containing the positional and keywoard arguments
34 passed to the handler in the event listener."""
35
36 def __init__(self, type, *args, **kwargs):
37 self.type = type
38 self.args = args
39 self.kwargs = kwargs
40
41
42class Listener:
43 """Event listener notified when events are published by the dispatcher."""
44
45 def __init__(self, event_type, handler, count):
46 self.event_type = event_type
47 self.handler = handler
48 self.count = count
49
50 def notify(self, event):
51 """Notify the handler with the payload of the event.
52
53 :param event: The event containint the payload for the handler.
54 """
55 if self.count is None or self.count:
56 self.handler(*event.args, **event.kwargs)
57 if self.count:
58 self.count -= 1
59
60
61class ListenerList(Listener):
62 """Event listener notified for lists of events."""
63
64 def __init__(self, *args, **kwargs):
65 super(ListenerList, self).__init__(*args, **kwargs)
66 self.event_types = set(self.event_type)
67 self.kwargs = {}
68
69 def notify(self, event):
70 """Only notify the handler when all the events for this listener
71 have been published by the dispatcher. When duplicate events
72 occur, the latest event is preserved and the previous one are
73 overwritten until all events have been published.
74 """
75 if self.count is None or self.count:
76 self.kwargs[event.type] = event.args[0]
77 if self.event_types.issubset(self.kwargs):
78 self.handler(**self.kwargs)
79 if self.count:
80 self.count -= 1
81
82
83class ListenerQueue(ListenerList):
84
85 def notify(self, event):
86 """Only notify the handler when all the events for this listener
87 have been published by the dispatcher. Duplicate events are enqueued
88 and dequeued only when all events have been published.
89 """
90 arg = event.args[0]
91 queue = self.kwargs.setdefault(event.type, [])
92
93 # Strip duplicates from the queue.
94 if arg not in queue:
95 queue.append(arg)
96
97 # Once the queue has handler has been called, the queue
98 # then behaves like a list using the latest events.
99 if self.event_types.issubset(self.kwargs):
100 self.notify = notify = super(ListenerQueue, self).notify
101 keys = list(self.kwargs.keys())
102 for values in product(*list(self.kwargs.values())):
103 self.kwargs = dict(list(zip(keys, values)))
104 notify(event)
105
106
107class Dispatcher:
108 """Register handlers and publish events for them identified by strings."""
109
110 listener_factory = Listener
111
112 def __init__(self, listener_factory=None):
113 self._event_listeners = {}
114
115 if listener_factory is not None:
116 self.listener_factory = listener_factory
117
118 def registerHandler(self, event_type, handler, count=None):
119 """Register an event handler and return its listener.
120
121 :param event_type: The name of the event type to handle.
122 :param handler: The function handling the given event type.
123 :param count: Optionally, the number times to call the handler.
124 """
125 listener = self.listener_factory(event_type, handler, count)
126
127 listeners = self._event_listeners.setdefault(event_type, [])
128 listeners.append(listener)
129
130 return listener
131
132 def unregisterHandler(self, handler):
133 """Unregister a handler.
134
135 :param handler: The handler to unregister.
136 """
137 for event_type, listeners in self._event_listeners.items():
138 listeners = [
139 listener for listener in listeners
140 if listener.handler == handler]
141 if listeners:
142 self._event_listeners[event_type] = listeners
143 else:
144 del self._event_listeners[event_type]
145
146 def unregisterListener(self, listener, event_type=None):
147 """Unregister a listener.
148
149 :param listener: The listener of the handler to unregister.
150 :param event_type: Optionally, the event_type to unregister.
151 """
152 if event_type is None:
153 event_type = listener.event_type
154
155 self._event_listeners[event_type].remove(listener)
156 if not self._event_listeners[event_type]:
157 del self._event_listeners[event_type]
158
159 def publishEvent(self, event_type, *args, **kwargs):
160 """Publish an event of a given type and notify all listeners.
161
162 :param event_type: The name of the event type to publish.
163 :param args: Positional arguments to pass to the registered handlers.
164 :param kwargs: Keyword arguments to pass to the registered handlers.
165 """
166 if event_type in self._event_listeners:
167 event = Event(event_type, *args, **kwargs)
168 for listener in list(self._event_listeners[event_type]):
169 try:
170 listener.notify(event)
171 if listener.count is not None and not listener.count:
172 self.unregisterListener(listener)
173 except:
174 logging.exception(
175 "Error running event handler for %r with args %r %r",
176 event_type, args, kwargs)
177
178
179class DispatcherList(Dispatcher):
180 """
181 Register handlers and publish events for them identified by lists
182 of strings.
183 """
184
185 listener_factory = ListenerList
186
187 def registerHandler(self, event_types, handler, count=None):
188 """See Dispatcher."""
189 if not isinstance(event_types, (list, tuple)):
190 event_types = (event_types,)
191
192 listener = self.listener_factory(event_types, handler, count)
193 for event_type in event_types:
194 listeners = self._event_listeners.setdefault(event_type, [])
195 listeners.append(listener)
196
197 return listener
198
199 def unregisterListener(self, listener):
200 """See Dispatcher."""
201 for event_type in listener.event_types:
202 super(DispatcherList, self).unregisterListener(
203 listener, event_type)
204
205 def publishEvent(self, event_type, arg):
206 """See Dispatcher."""
207 super(DispatcherList, self).publishEvent(event_type, arg)
208
209
210class DispatcherQueue(DispatcherList):
211 """
212 Register handlers and publish events for them identified by lists
213 of strings in queue order.
214 """
215
216 listener_factory = ListenerQueue
0217
=== added directory 'checkbox/heuristics'
=== added file 'checkbox/heuristics/__init__.py'
--- checkbox/heuristics/__init__.py 1970-01-01 00:00:00 +0000
+++ checkbox/heuristics/__init__.py 2013-10-01 18:38:21 +0000
@@ -0,0 +1,56 @@
1# This file is part of Checkbox.
2#
3# Copyright 2012 Canonical Ltd.
4# Written by:
5# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
6#
7# Checkbox is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# Checkbox is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
19
20"""
21checkbox.heuristics
22===================
23
24This module contains implementations behind various heuristics used throughout
25the code. The intent of this module is twofold:
26
27 1) To encourage code reuse so that developers can use one implementation of
28 "guesswork" that is sometimes needed in our test. This reduces duplicate
29 bugs where many scripts do similar things in a different way.
30
31 2) To identify missing features in plumbing layer APIs such as
32 udev/udisks/dbus etc. Ideally no program should have to guess this, the
33 plumbing layer should be able to provide this meta data to allow
34 application developers deliver consistent behavior across userspace.
35
36Heuristics should be reusable from both python and shell. To make that possible
37each heuristics needs to be constrained to serializable input and output. This
38levels the playing field and allows both shell developers and python developers
39to reuse the same function.
40
41Additionally heuristics should try to avoid accessing thick APIs (such as
42objects returned by various libraries. This is meant to decrease the likelihood
43that updates to those libraries break this code. As an added side effect this
44also should make the implementation more explicit and easier to understand.
45
46In the long term each heuristic should be discussed with upstream developers of
47the particular problem area (udev, udisks, etc) to see if that subsystem can
48provide the required information directly, without us having to guess and fill
49the gaps.
50
51Things to consider when adding entries to this package:
52
53 1) File a bug on the upstream package about missing feature.
54
55 2) File a bug on checkbox to de-duplicate similar heuristics
56"""
057
=== added directory 'checkbox/heuristics/tests'
=== added file 'checkbox/heuristics/tests/__init__.py'
=== added file 'checkbox/heuristics/tests/test_udisks2.py'
--- checkbox/heuristics/tests/test_udisks2.py 1970-01-01 00:00:00 +0000
+++ checkbox/heuristics/tests/test_udisks2.py 2013-10-01 18:38:21 +0000
@@ -0,0 +1,40 @@
1# This file is part of Checkbox.
2#
3# Copyright 2012 Canonical Ltd.
4# Written by:
5# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
6#
7# Checkbox is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# Checkbox is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
19
20"""
21
22checkbox.heuristics.tests.test_udisks2
23======================================
24
25Unit tests for checkbox.heuristics.udisks2 module
26"""
27
28from unittest import TestCase
29
30from checkbox.heuristics.udisks2 import is_memory_card
31
32
33class TestIsMemoryCard(TestCase):
34
35 def test_generic(self):
36 """
37 Device with vendor string "GENERIC" is a memory card
38 """
39 self.assertTrue(
40 is_memory_card(vendor="Generic", model="", udisks2_media=None))
041
=== added file 'checkbox/heuristics/udev.py'
--- checkbox/heuristics/udev.py 1970-01-01 00:00:00 +0000
+++ checkbox/heuristics/udev.py 2013-10-01 18:38:21 +0000
@@ -0,0 +1,44 @@
1# This file is part of Checkbox.
2#
3# Copyright 2012 Canonical Ltd.
4# Written by:
5# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
6#
7# Checkbox is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# Checkbox is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
19
20"""
21checkbox.heuristics.dev
22=======================
23
24Heuristics for udev.
25
26 Documentation: http://udisks.freedesktop.org/docs/latest/
27 Source code: http://cgit.freedesktop.org/systemd/systemd/ (src/udev)
28 Bug tracker: http://bugs.freedesktop.org/ (using systemd product)
29"""
30
31
32def is_virtual_device(device_file):
33 """
34 Given a device name like /dev/ramX, /dev/sdX or /dev/loopX determine if
35 this is a virtual device. Virtual devices are typically uninteresting to
36 users. The only exception may be nonempty loopback device.
37
38 Possible prior art: gnome-disks, palimpset (precursor, suffering from this
39 flaw and showing all the /dev/ram devices by default)
40 """
41 for part in device_file.split("/"):
42 if part.startswith("ram") or part.startswith("loop"):
43 return True
44 return False
045
=== added file 'checkbox/heuristics/udisks2.py'
--- checkbox/heuristics/udisks2.py 1970-01-01 00:00:00 +0000
+++ checkbox/heuristics/udisks2.py 2013-10-01 18:38:21 +0000
@@ -0,0 +1,62 @@
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches