oneconf-0.3.9/0000775000000000000000000000000012624561776010062 5ustar oneconf-0.3.9/AUTHORS0000664000000000000000000000017112624560235011116 0ustar The following people worked on this software: Matthew Paul Thomas - Design Michael Vogt - Coding Didier Roche - Coding oneconf-0.3.9/test/0000775000000000000000000000000012624561776011041 5ustar oneconf-0.3.9/test/test_mainfeatures.py0000664000000000000000000003331712624560235015131 0ustar #!/usr/bin/env python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- ### BEGIN LICENSE # Copyright (C) 2011 Didier Roche # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . ### END LICENSE import atexit import errno import json import os import shutil import sys import subprocess import unittest from gettext import gettext as _ try: from unittest.mock import patch except ImportError: # Python 2 from mock import patch sys.path.insert(0, os.path.abspath('.')) # Create the override file, but ensure that it gets cleaned up when this test # exits. Because of the way oneconf.paths operates, this file must exist # before the import. def cleanup(): try: os.remove('/tmp/oneconf.override') except OSError as error: if error.errno != errno.ENOENT: raise try: shutil.rmtree('/tmp/oneconf-test') except OSError as error: if error.errno != errno.ENOENT: raise atexit.register(cleanup) shutil.copy( os.path.join(os.path.dirname(__file__), "data", "oneconf.override"), "/tmp/oneconf.override") from oneconf import paths from oneconf.hosts import HostError, Hosts from oneconf.directconnect import DirectConnect class IntegrationTests(unittest.TestCase): def setUp(self): self.dbus_service_process = None self.hostid = "0000" self.hostname = "foomachine" os.environ["ONECONF_HOST"] = "%s:%s" % (self.hostid, self.hostname) self.oneconf = DirectConnect() self.hostdir = os.path.join(paths.ONECONF_CACHE_DIR, self.hostid) src = os.path.join(os.path.dirname(__file__), "data", "hostdata") shutil.copytree(src, self.hostdir) self.src_hostdir = None def tearDown(self): shutil.rmtree(os.path.dirname(paths.ONECONF_CACHE_DIR)) def is_same_logo_than_original(self): src_content = open(os.path.join(os.path.dirname(__file__), "data", "hostdata", "%s_%s.png" % (paths.LOGO_PREFIX, self.hostid))).readlines() dest_content = open(os.path.join(paths.ONECONF_CACHE_DIR, self.hostid, "%s_%s.png" % (paths.LOGO_PREFIX, self.hostid))).readlines() return (src_content == dest_content) def copy_state(self, test_ident): '''Set state from the test identifier.''' datadir = os.path.join(os.path.dirname(__file__), "data", "integrationdatatests") self.src_hostdir = os.path.join(datadir, 'host_%s' % test_ident) self.result_hostdir = os.path.join(datadir, 'resulthost_%s' % test_ident) shutil.rmtree(os.path.dirname(paths.ONECONF_CACHE_DIR)) shutil.copytree(self.src_hostdir, self.hostdir) def test_load_host_data(self): """Load existing hosts data, check that nothing change for current host as well """ hosts = self.oneconf.get_all_hosts() self.assertEqual(hosts, { u'AAAAAA': (False, u'julie-laptop', True), u'BBBBBB': (False, u'yuna', True), '0000': (True, 'foomachine', True), }) # check that nothing changed host_file = os.path.join(paths.ONECONF_CACHE_DIR, self.hostid, paths.HOST_DATA_FILENAME) with open(host_file, 'r') as f: current_host = json.load(f) self.assertEqual(current_host['hostid'], self.hostid) self.assertEqual(current_host['hostname'], self.hostname) self.assertEqual( current_host['packages_checksum'], "9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b") self.assertEqual(current_host['share_inventory'], True) # XXX PIL is not available for Python 3, so there will be no # logo_checksum. ## self.assertEqual( ## current_host['logo_checksum'], ## 'c7e18f80419ea665772fef10e347f244d5ba596cc2764a8e611603060000000000.000042') ## self.assertTrue(self.is_same_logo_than_original()) def test_create_new_host(self): '''Creating a new host, for oneconf first run''' shutil.rmtree(os.path.dirname(paths.ONECONF_CACHE_DIR)) self.oneconf.get_all_hosts() host_file = os.path.join( paths.ONECONF_CACHE_DIR, self.hostid, paths.HOST_DATA_FILENAME) with open(host_file, 'r') as f: current_host = json.load(f) self.assertEqual(current_host['hostid'], self.hostid) self.assertEqual(current_host['hostname'], self.hostname) self.assertEqual(current_host['packages_checksum'], None) self.assertEqual(current_host['share_inventory'], False) # XXX PIL is not available for Python 3, so there will be no # logo_checksum. ## self.assertEqual( ## current_host['logo_checksum'], ## 'c7e18f80419ea665772fef10e347f244d5ba596cc2764a8e611603060000000000.000042') ## self.assertTrue(self.is_same_logo_than_original()) def test_update_host(self): '''Update an existing hostid and hostname, checking that the "host" file is changed''' self.oneconf.update() host_file = os.path.join(paths.ONECONF_CACHE_DIR, self.hostid, paths.HOST_DATA_FILENAME) with open(host_file, 'r') as f: current_host = json.load(f) self.assertEqual(current_host['hostid'], self.hostid) self.assertEqual(current_host['hostname'], self.hostname) self.assertEqual(current_host["packages_checksum"], "60f28c520e53c65cc37e9b68fe61911fb9f73ef910e08e988cb8ad52") self.assertEqual(current_host["share_inventory"], True) # XXX PIL is not available for Python 3, so there will be no # logo_checksum. ## self.assertEqual(current_host['logo_checksum'], 'c7e18f80419ea665772fef10e347f244d5ba596cc2764a8e611603060000000000.000042') ## self.assertTrue(self.is_same_logo_than_original()) def test_diff_host(self): """Create a diff between current host and AAAAA. This handle the case with auto and manual packages """ self.assertEqual(self.oneconf.diff('AAAAAA', None), ([u'libqtdee2', u'ttf-lao'], [u'bar', u'baz'])) def test_diff_with_no_valid_host(self): '''Test with no valid host''' from oneconf.packagesethandler import PackageSetHandler self.assertRaises(HostError, PackageSetHandler().diff, 'A') def test_with_only_localhost(self): '''List machine with only localhost''' shutil.rmtree(os.path.dirname(paths.ONECONF_CACHE_DIR)) self.oneconf.update() self.assertEqual(len(self.oneconf.get_all_hosts()), 1) def test_list_packages(self): '''List packages for machine with default options''' self.assertEqual(self.oneconf.get_packages(self.hostid, None, False), {u'baz': {u'auto': False}, u'foo': {u'auto': False}, u'bar': {u'auto': True}}) def test_list_packages_manual_only(self): '''List packages for machine for only manual package''' # FIXME: the result is not in the same format, that sux… self.assertEqual( sorted(self.oneconf.get_packages(self.hostid, None, True)), [u'baz', u'foo']) def test_list_invalid_machine(self): '''List packages for an invalid machine''' from oneconf.packagesethandler import PackageSetHandler self.assertRaises(HostError, PackageSetHandler().get_packages, 'A') def test_list_machine_by_hostname(self): '''List packages for machine using hostname''' list_pkg1 = self.oneconf.get_packages(self.hostid, None, False) list_pkg2 = self.oneconf.get_packages(None, self.hostname, False) self.assertEqual(list_pkg1, list_pkg2) def test_diff_between_me_and_me(self): '''Diff between the same computer which should end up in an empty list''' self.assertEqual(self.oneconf.diff(self.hostid, None), ('', '')) def test_disable_enable_inventory_for_current_host(self): '''Try to disable and the inventory for 0000 host (current host)''' hosts = self.oneconf.get_all_hosts() self.assertEqual(hosts, {u'AAAAAA': (False, u'julie-laptop', True), u'BBBBBB': (False, u'yuna', True), '0000': (True, 'foomachine', True)}) self.oneconf.set_share_inventory(False, '0000') hosts = self.oneconf.get_all_hosts() self.assertEqual(hosts, {u'AAAAAA': (False, u'julie-laptop', True), u'BBBBBB': (False, u'yuna', True), '0000': (True, 'foomachine', False)}) # software-center does it without using the hostid self.oneconf.set_share_inventory(True, '') hosts = self.oneconf.get_all_hosts() self.assertEqual(hosts, {u'AAAAAA': (False, u'julie-laptop', True), u'BBBBBB': (False, u'yuna', True), '0000': (True, 'foomachine', True)}) host_file = os.path.join(paths.ONECONF_CACHE_DIR, self.hostid, paths.HOST_DATA_FILENAME) with open(host_file, 'r') as f: current_host = json.load(f) self.assertEqual(current_host['share_inventory'], True) self.assertFalse(os.path.isfile(os.path.join(paths.ONECONF_CACHE_DIR, self.hostid, paths.PENDING_UPLOAD_FILENAME))) def test_disable_enable_inventory_for_other_host(self): '''Try to disable the current inventory for another host (put the request in pending) and then enable it again''' hosts = self.oneconf.get_all_hosts() self.assertEqual(hosts, {u'AAAAAA': (False, u'julie-laptop', True), u'BBBBBB': (False, u'yuna', True), '0000': (True, 'foomachine', True)}) self.oneconf.set_share_inventory(False, 'AAAAAA') with open(os.path.join(paths.ONECONF_CACHE_DIR, self.hostid, paths.PENDING_UPLOAD_FILENAME), 'r') as f: self.assertEqual(json.load(f), {u'AAAAAA': {u'share_inventory': False}}) def test_dummy_last_sync_state(self): '''Get a dummy last sync state''' self.assertEqual(self.oneconf.get_last_sync_date(), '123456789.00') def test_bootstrap_without_any_sync(self): '''Bootstrap without any sync before''' os.remove(os.path.join(paths.ONECONF_CACHE_DIR, self.hostid, paths.LAST_SYNC_DATE_FILENAME)) self.assertEqual(self.oneconf.get_last_sync_date(), 'Was never synced') def test_daemon_query_cant_run_root(self): '''Test that the daemon or the querier can't run as root''' self.assertEqual(subprocess.call(["fakeroot", "./oneconf-query"]), 1) self.assertEqual(subprocess.call(["fakeroot", "./oneconf-service"]), 1) def test_broken_host_file(self): '''Test that we recreate a new host file if the file is broken''' self.copy_state('brokenhostfile') self.assertEqual(self.oneconf.get_all_hosts(), {self.hostid: (True, self.hostname, False)}) host_file = os.path.join(paths.ONECONF_CACHE_DIR, self.hostid, paths.HOST_DATA_FILENAME) with open(host_file, 'r') as f: current_host = json.load(f) self.assertEqual(current_host['hostid'], self.hostid) self.assertEqual(current_host['hostname'], self.hostname) self.assertEqual(current_host['packages_checksum'], None) self.assertEqual(current_host['share_inventory'], False) def test_broken_otherhosts_file(self): '''Test that we discare the other hosts file if the file is broken (a future sync will rewrite it)''' self.copy_state('brokenotherhosts') host = Hosts() self.assertEqual(host._load_other_hosts(), {}) def test_broken_pending_file(self): '''Test that we discare the other hosts file if the file is broken (a future sync will rewrite it)''' self.copy_state('brokenpending') host = Hosts() self.assertEqual(host.get_hostid_pending_change('foo', 'bar'), None) host.add_hostid_pending_change({'foo': {'bar': 'baz'}}) self.assertEqual(host.get_hostid_pending_change('foo', 'bar'), 'baz') def test_broken_latestsync_file(self): '''Test that we return a dummy latest sync date if we can't load it. Next update will rewrite it''' self.copy_state('brokenlatestsync') host = Hosts() self.assertEqual(host.get_last_sync_date(), _("Was never synced")) def test_broken_packageset_file(self): """Test that we discare the other hosts file if the file is broken (a future sync will rewrite it) """ self.copy_state('brokenpackageset') from oneconf.packagesethandler import PackageSetHandler packageset = PackageSetHandler() self.assertEqual( packageset._get_packagelist_from_store(self.hostid), {'foo': {'auto': False}, 'pool': {'auto': True}}) self.assertEqual( packageset.hosts.current_host['packages_checksum'], '60f28c520e53c65cc37e9b68fe61911fb9f73ef910e08e988cb8ad52') @patch('oneconf.hosts.FAKE_WALLPAPER', '/wallpaper-doesnt-exist.png') @patch('oneconf.hosts.FAKE_WALLPAPER_MTIME', None) def test_no_valid_wallpaper(self): '''Test that no crash occurs with an invalid wallpaper URL''' host = Hosts() self.assertEqual(host._get_current_wallpaper_data(), (None, None)) # TODO: ensure a logo is updated # # main # if __name__ == '__main__': print(''' ######################################### # Main OneConf tests # ######################################### ''') unittest.main(exit=False) os.remove("/tmp/oneconf.override") oneconf-0.3.9/test/test_syncing.py0000664000000000000000000006217612624560235014125 0ustar #!/usr/bin/env python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- ### BEGIN LICENSE # Copyright (C) 2011 Didier Roche # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . ### END LICENSE import atexit import errno import json import os import shutil import sys import subprocess import time import unittest # For Python 2, because builtin-open has no 'encoding' argument. import codecs sys.path.insert(0, os.path.abspath('.')) # Create the override file, but ensure that it gets cleaned up when this test # exits. Because of the way oneconf.paths operates, this file must exist # before the import. def cleanup(): try: os.remove('/tmp/oneconf.override') except OSError as error: if error.errno != errno.ENOENT: raise try: shutil.rmtree('/tmp/oneconf-test') except OSError as error: if error.errno != errno.ENOENT: raise atexit.register(cleanup) shutil.copy( os.path.join(os.path.dirname(__file__), "data", "oneconf.override"), '/tmp/oneconf.override') from oneconf import paths from oneconf.networksync.fake_webcatalog_silo import FakeWebCatalogSilo class OneConfSyncing(unittest.TestCase): def setUp(self): os.environ['PYTHONPATH'] = '.:' + os.environ.get('PYTHONPATH', '') self.cmd_line = [sys.executable, '-m', 'oneconf.networksync'] self.hostid = "0000" self.hostname = "foomachine" self.output = None os.environ["ONECONF_HOST"] = "%s:%s" % (self.hostid, self.hostname) os.environ["ONECONF_NET_CONNECTED"] = "True" os.environ["ONECONF_SSO_CRED"] = "True" self.hostdir = os.path.join(paths.ONECONF_CACHE_DIR, self.hostid) self.src_hostdir = None def tearDown(self): for key in list(os.environ.keys()): if "ONECONF_" in key: os.environ.pop(key) try: shutil.rmtree(os.path.dirname(paths.ONECONF_CACHE_DIR)) except OSError: pass def collect_debug_output(self, process): '''Get the full stderr output from a process''' output = [] while True: additional_output = process.stderr.readline() print(additional_output) if not additional_output: break output.append(additional_output) return output def msg_in_output(self, output, msg): for line in output: if msg in line: return True return False def get_daemon_output(self): '''Return the daemon output and ensure it's stopped''' p = subprocess.Popen(self.cmd_line, stderr=subprocess.PIPE, universal_newlines=True) output = self.collect_debug_output(p) p.wait() p = None return output def check_msg_in_output(self, msg, check_errors=True): '''launch the subprocess and check if the msg is present in the output''' if not self.output: self.output = self.get_daemon_output() # ensure there is no traceback or error self.assertFalse(self.msg_in_output(self.output, 'Traceback')) if check_errors: self.assertFalse(self.msg_in_output(self.output, 'ERROR:')) return self.msg_in_output(self.output, msg) def copy_state(self, test_ident): '''Set state from the test identifier.''' datadir = os.path.join( os.path.dirname(__file__), "data", "syncdatatests") self.src_hostdir = os.path.join(datadir, 'host_%s' % test_ident) self.result_hostdir = os.path.join( datadir, 'resulthost_%s' % test_ident) shutil.copytree(self.src_hostdir, self.hostdir) if not os.path.isdir(paths.WEBCATALOG_SILO_DIR): os.makedirs(paths.WEBCATALOG_SILO_DIR) try: shutil.copy(os.path.join(datadir, 'silo_%s' % test_ident), paths.WEBCATALOG_SILO_SOURCE) # For Python 2 - Python 3 could use FileNotFoundError except (IOError, OSError) as error: if error.errno != errno.ENOENT: pass # some tests have no silo source file def compare_silo_results(self, hosts_metadata, packages_metadata): """Return True if start and result silos contains identical hosts and pkg""" fakecatalog = FakeWebCatalogSilo(paths.WEBCATALOG_SILO_RESULT) self.assertEqual(fakecatalog._FAKE_SETTINGS['hosts_metadata'], hosts_metadata) self.assertEqual(fakecatalog._FAKE_SETTINGS['packages_metadata'], packages_metadata) def compare_files(self, file1, file2): '''Compare file content''' # We have to be moderately intellegent in comparing these files. Some # of them are json serialized dictionaries, so straight textual # comparisons won't work (dictionary order is not predictable). # Unfortunately, there's no way to know whether we're looking at json # or something else. So, first try to deserialize the content and if # that fails, use the string representations. # # Yes, we have to use a backslash here. :\ try: with codecs.open(file1, 'r', encoding='utf-8') as fp1, \ codecs.open(file2, 'r', encoding='utf-8') as fp2: src_string = fp1.read() dst_string = fp2.read() except UnicodeDecodeError: # If it wasn't even utf-8, then it isn't json, so re-open the # files as binary and compare the byte strings. with open(file1, 'rb') as fp1, open(file2, 'rb') as fp2: src_content = fp1.read() dst_content = fp2.read() self.assertEqual(src_content, dst_content) return try: src_content = json.loads(src_string) dst_content = json.loads(dst_string) except TypeError: # Again, it's not json. self.assertMultiLineEqual(src_string, dst_string) else: self.assertEqual(src_content, dst_content) def compare_dirs(self, source, dest): '''Compare directory files, ignoring the last_sync file on purpose''' for filename in os.listdir(source): if filename == paths.LAST_SYNC_DATE_FILENAME: continue self.compare_files(os.path.join(source, filename), os.path.join(dest, filename)) def test_no_sync_no_network(self): '''Test that no sync is happening if no network''' os.environ["ONECONF_NET_CONNECTED"] = "False" self.assertFalse(self.check_msg_in_output("Start processing sync")) def test_no_sync_no_sso(self): '''Test that no sync is happening if no sso''' os.environ["ONECONF_SSO_CRED"] = "False" self.assertFalse(self.check_msg_in_output("Start processing sync")) def test_sync_with_network_and_sso(self): '''Test that a sync is there if we have network and sso enabled''' self.assertTrue(self.check_msg_in_output("Start processing sync")) def test_first_sync(self): '''Test a first synchronisation without any data on the webcatalog''' self.copy_state('nosilo_nopackage_onlyhost') self.assertTrue( self.check_msg_in_output("Push current host to infra now")) self.assertTrue(self.check_msg_in_output("New host registered done")) self.assertFalse(self.check_msg_in_output("emit_new_hostlist")) self.assertFalse(self.check_msg_in_output("emit_new_packagelist")) self.assertFalse(self.check_msg_in_output("emit_new_logo")) self.compare_silo_results({self.hostid: {'hostname': self.hostname, 'logo_checksum': None, 'packages_checksum': None}}, {}) # Ensure nothing changed in the source dir self.compare_dirs(self.src_hostdir, self.hostdir) def test_date_synchro(self): '''Ensure a synchro date is written, older than current time, and right signal emitted''' self.copy_state('nosilo_nopackage_onlyhost') now = time.time() sync_file = os.path.join(self.hostdir, paths.LAST_SYNC_DATE_FILENAME) self.assertTrue(self.check_msg_in_output("Saving updated %s to disk" % sync_file)) self.assertTrue(self.check_msg_in_output("emit_new_lastestsync")) with open(sync_file, 'r') as f: contents = json.load(f) self.assertGreater(float(contents['last_sync']), now) def test_host_not_shared(self): '''Test a non shared host is really not shared''' self.copy_state('nosilo_nopackage_onlyhost_noshare') self.assertFalse(self.check_msg_in_output("Push current host to infra now")) self.assertFalse(self.check_msg_in_output("New host registered done")) self.assertTrue(self.check_msg_in_output("Ensure that current host is not shared")) self.compare_silo_results({}, {}) self.compare_dirs(self.src_hostdir, self.hostdir) # Ensure nothing changed in the source dir def test_unshare_shared_host(self): '''Share a host, and then unshare it. Check that everything is cleaned in the silo''' self.copy_state('previously_shared_notshared') self.assertTrue(self.check_msg_in_output("Ensure that current host is not shared")) self.assertFalse(self.check_msg_in_output("Can't delete current host from infra: Host Not Found")) self.compare_silo_results({}, {}) def test_share_host_with_packages(self): '''Share the current host with a package list''' self.copy_state('with_packages') self.assertTrue(self.check_msg_in_output("Check if packages for current host need to be pushed to infra")) self.assertTrue(self.check_msg_in_output("Push new packages")) self.compare_silo_results({self.hostid: {'hostname': self.hostname, 'logo_checksum': None, 'packages_checksum': u'9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b'}}, {self.hostid: {u'bar': {u'auto': True}, u'baz': {u'auto': False}, u'foo': {u'auto': False}}}) self.compare_dirs(self.src_hostdir, self.hostdir) # Ensure nothing changed in the source dir def test_unshare_host_with_packages(self): '''Unshare an existing host with packages''' self.copy_state('previously_shared_with_packages_notshared') self.assertTrue(self.check_msg_in_output("Ensure that current host is not shared")) self.assertFalse(self.check_msg_in_output("Can't delete current host from infra: Host Not Found")) self.compare_silo_results({}, {}) def test_unshare_other_host(self): '''Unshare a host which is not the current one''' self.copy_state('unshare_other_host') self.assertTrue(self.check_msg_in_output("Removing machine AAAA requested as a pending change")) self.assertTrue(self.check_msg_in_output("No more pending changes remaining, removing the file")) self.compare_silo_results({}, {}) def test_unshare_other_host_error(self): '''Unshare a host which is not the current one, raising an exception and keep it to the list of unsharing''' self.copy_state('unshare_other_host') os.environ["ONECONF_delete_machine_error"] = "True" self.assertTrue(self.check_msg_in_output("Removing machine AAAA requested as a pending change", check_errors=False)) self.assertTrue(self.check_msg_in_output("WebClient server doesn't want to remove hostid (AAAA): Fake WebCatalogAPI raising fake exception", check_errors=False)) self.assertFalse(self.check_msg_in_output("No more pending changes remaining, removing the file", check_errors=False)) self.compare_silo_results({'AAAA': {'hostname': 'aaaa', 'logo_checksum': None, 'packages_checksum': 'packageaaaa'}}, {'AAAA': {u'bar': {u'auto': True}, 'baz': {u'auto': False}, 'foo': {u'auto': False}}}) self.compare_files(os.path.join(self.src_hostdir, paths.PENDING_UPLOAD_FILENAME), os.path.join(self.hostdir, paths.PENDING_UPLOAD_FILENAME)) def test_update_host_no_change(self): '''Update a host without any change''' self.copy_state('only_current_host') self.assertTrue(self.check_msg_in_output("Check if packages for current host need to be pushed to infra")) self.assertFalse(self.check_msg_in_output("No more pending changes remaining, removing the file")) self.assertFalse(self.check_msg_in_output("Push new")) self.compare_silo_results({self.hostid: {'hostname': self.hostname, 'logo_checksum': None, 'packages_checksum': u'9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b'}}, {u'0000': {u'bar': {u'auto': True}, u'baz': {u'auto': False}, u'foo': {u'auto': False}}}) def test_update_host_with_hostname_change(self): '''Update a host only changing the hostname''' self.hostname = "barmachine" os.environ["ONECONF_HOST"] = "%s:%s" % (self.hostid, self.hostname) self.copy_state('update_current_hostname') self.assertTrue(self.check_msg_in_output("Host data refreshed")) self.assertFalse(self.check_msg_in_output("Push new")) self.compare_silo_results({self.hostid: {'hostname': self.hostname, 'logo_checksum': None, 'packages_checksum': u'9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b'}}, {self.hostid: {u'bar': {u'auto': True}, u'baz': {u'auto': False}, u'foo': {u'auto': False}}}) def test_update_packages_for_host(self): '''Update a package list for current host''' self.copy_state('update_packages_for_current_host') self.assertTrue(self.check_msg_in_output( "Check if packages for current host need to be pushed to infra")) self.assertTrue(self.check_msg_in_output("Push new packages")) self.compare_silo_results( {self.hostid: {'hostname': self.hostname, 'logo_checksum': None, 'packages_checksum': u'AAAA'}}, {self.hostid: {u'fol': {u'auto': False}, u'bar': {u'auto': True}, u'baz': {u'auto': True}}} ) def test_get_firsttime_sync_other_host(self): '''First time getting another host, no package''' self.copy_state('firsttime_sync_other_host') self.assertTrue(self.check_msg_in_output("Refresh new host")) self.assertTrue(self.check_msg_in_output("Saving updated /tmp/oneconf-test/cache/0000/other_hosts to disk")) self.assertFalse(self.check_msg_in_output("Refresh new packages")) self.assertFalse(self.check_msg_in_output("Refresh new logo")) self.assertTrue(self.check_msg_in_output("emit_new_hostlist not bound to anything")) self.compare_dirs(self.result_hostdir, self.hostdir) def test_sync_other_host_with_packages(self): '''Sync another host with packages''' self.copy_state('sync_other_host_with_packages') self.assertTrue(self.check_msg_in_output("Refresh new packages")) self.assertTrue(self.check_msg_in_output("Saving updated /tmp/oneconf-test/cache/0000/package_list_AAAA to disk")) self.assertTrue(self.check_msg_in_output("Refresh new host")) self.assertTrue(self.check_msg_in_output("Saving updated /tmp/oneconf-test/cache/0000/other_hosts to disk")) self.assertTrue(self.check_msg_in_output("emit_new_hostlist not bound to anything")) self.assertTrue(self.check_msg_in_output("emit_new_packagelist(AAAA) not bound to anything")) self.compare_dirs(self.result_hostdir, self.hostdir) def test_sync_other_host_with_updated_packages(self): '''Sync another host with updated packages''' self.copy_state('sync_other_host_with_updated_packages') self.assertTrue(self.check_msg_in_output("Refresh new packages")) self.assertTrue(self.check_msg_in_output("Saving updated /tmp/oneconf-test/cache/0000/package_list_AAAA to disk")) self.assertTrue(self.check_msg_in_output("Refresh new host")) self.assertTrue(self.check_msg_in_output("Saving updated /tmp/oneconf-test/cache/0000/other_hosts to disk")) self.assertTrue(self.check_msg_in_output("emit_new_hostlist not bound to anything")) self.assertTrue(self.check_msg_in_output("emit_new_packagelist(AAAA) not bound to anything")) self.compare_dirs(self.result_hostdir, self.hostdir) def test_sync_other_host_with_updated_hostname(self): '''Sync another host with updated hostname''' self.copy_state('sync_other_host_with_updated_hostname') self.assertFalse(self.check_msg_in_output("Refresh new packages")) self.assertTrue(self.check_msg_in_output("Refresh new host")) self.assertTrue(self.check_msg_in_output("Saving updated /tmp/oneconf-test/cache/0000/other_hosts to disk")) self.assertTrue(self.check_msg_in_output("emit_new_hostlist not bound to anything")) self.compare_dirs(self.result_hostdir, self.hostdir) def test_sync_a_newhost_with_already_other_hosts(self): '''Add an additional host with some already there''' self.copy_state('sync_a_newhost_with_already_other_hosts') self.assertTrue(self.check_msg_in_output("Refresh new packages")) self.assertFalse(self.check_msg_in_output("Saving updated /tmp/oneconf-test/cache/0000/package_list_AAAA to disk")) self.assertTrue(self.check_msg_in_output("Saving updated /tmp/oneconf-test/cache/0000/package_list_BBBB to disk")) self.assertTrue(self.check_msg_in_output("Refresh new host")) self.assertTrue(self.check_msg_in_output("Saving updated /tmp/oneconf-test/cache/0000/other_hosts to disk")) self.assertTrue(self.check_msg_in_output("emit_new_hostlist not bound to anything")) self.compare_dirs(self.result_hostdir, self.hostdir) def test_sync_remove_other_host(self): '''Remove a host after a sync''' self.copy_state('sync_remove_other_host') self.assertTrue(self.check_msg_in_output("Refresh new host")) self.assertTrue(self.check_msg_in_output( "Saving updated /tmp/oneconf-test/cache/0000/other_hosts to disk")) self.assertTrue(self.check_msg_in_output( "emit_new_hostlist not bound to anything")) self.assertFalse(self.check_msg_in_output("emit_new_packagelist")) self.compare_dirs(self.result_hostdir, self.hostdir) def test_server_error(self): '''Test server not responsing at all''' self.copy_state('fake_server_errors') os.environ["ONECONF_server_response_error"] = "True" self.assertTrue(self.check_msg_in_output("WebClient server answer error: Fake WebCatalogAPI raising fake exception", check_errors=False)) self.assertFalse(self.check_msg_in_output("Saving updated", check_errors=False)) # Untouched silo and source dir self.compare_silo_results({self.hostid: {'hostname': 'barmachine', 'logo_checksum': None, 'packages_checksum': '9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b'}, 'AAAA': {'hostname': 'aaaa', 'logo_checksum': None, 'packages_checksum': 'packageaaaa'}, 'BBBB': {'hostname': 'toremove', 'logo_checksum': None, 'packages_checksum': 'toremove'}}, {self.hostid: {'bar': {'auto': True}, 'baz': {'auto': False}, 'foo': {'auto': False}}, 'AAAA': {'bar': {'auto': True}, 'baz': {'auto': False}, 'foo': {'auto': False}}, 'BBBB': {u'bar': {u'auto': False}}}) self.compare_dirs(self.src_hostdir, self.hostdir) def test_get_all_machines_error(self): '''Test when getting all machines errors, we should stop syncing''' self.copy_state('fake_server_errors') os.environ["ONECONF_list_machines_error"] = "True" self.assertTrue(self.check_msg_in_output("Invalid machine list from server, stopping sync: Fake WebCatalogAPI raising fake exception", check_errors=False)) # Stop the sync there self.assertFalse(self.check_msg_in_output("Saving updated", check_errors=False)) def test_get_packages_error(self): '''Test when getting all packages errors''' self.copy_state('fake_server_errors') os.environ["ONECONF_list_packages_error"] = "True" self.assertTrue(self.check_msg_in_output("Invalid package data from server: Fake WebCatalogAPI raising fake exception", check_errors=False)) self.assertTrue(self.check_msg_in_output("Saving updated", check_errors=False)) def test_delete_current_host_error(self): '''Try to delete the current host and there is an error''' self.copy_state('delete_current_host_error') os.environ["ONECONF_delete_machine_error"] = "True" self.assertTrue(self.check_msg_in_output("Can't delete current host from infra: Fake WebCatalogAPI raising fake exception", check_errors=False)) self.assertTrue(self.check_msg_in_output("Saving updated", check_errors=False)) def test_update_current_host_error(self): '''Update an already registered current host and there is an error''' self.copy_state('fake_server_errors') os.environ["ONECONF_update_machine_error"] = "True" self.assertTrue(self.check_msg_in_output("Can't update machine: Fake WebCatalogAPI raising fake exception", check_errors=False)) self.assertFalse(self.check_msg_in_output("Host data refreshed", check_errors=False)) self.assertTrue(self.check_msg_in_output("Saving updated", check_errors=False)) def test_create_current_host_error(self): '''Try to create the current host and there is an error''' self.copy_state('nosilo_nopackage_onlyhost') os.environ["ONECONF_update_machine_error"] = "True" self.assertTrue(self.check_msg_in_output("Can't register new host: Fake WebCatalogAPI raising fake exception", check_errors=False)) self.assertFalse(self.check_msg_in_output("New host registered done", check_errors=False)) self.assertTrue(self.check_msg_in_output("Saving updated", check_errors=False)) def test_update_package_list_error(self): '''Try to update the remove package list and get an error''' self.copy_state('fake_server_errors') os.environ["ONECONF_update_packages_error"] = "True" self.assertTrue(self.check_msg_in_output("Can't push current package list: Fake WebCatalogAPI raising fake exception", check_errors=False)) self.assertTrue(self.check_msg_in_output("Saving updated", check_errors=False)) def test_sync_with_broken_pending_file(self): '''Try to update with a broken pending file, should just ignore it''' self.copy_state('broken_pending_file') self.check_msg_in_output("The pending file is broken, ignoring", check_errors=False) def test_no_sync_with_invalid_setup(self): '''Test that no sync and no traceback is happening if we have an invalid setup''' self.cmd_line.append('--no-infra-client') shutil.copy(os.path.join(os.path.dirname(__file__), "data", "oneconf.invaliddistro.override"), "/tmp/oneconf.override") self.assertFalse(self.check_msg_in_output("Start processing sync")) # # main # if __name__ == '__main__': print(''' ######################################### # Test OneConf syncing # ######################################### ''') unittest.main(exit=False) os.remove("/tmp/oneconf.override") oneconf-0.3.9/test/test_daemon.py0000664000000000000000000001476712624560235013721 0ustar #!/usr/bin/env python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- ### BEGIN LICENSE # Copyright (C) 2011 Didier Roche # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . ### END LICENSE import atexit import errno import os import shutil import sys import subprocess import time import unittest sys.path.insert(0, os.path.abspath('.')) # Create the override file, but ensure that it gets cleaned up when this test # exits. Because of the way oneconf.paths operates, this file must exist # before the import. def cleanup(): try: os.remove('/tmp/oneconf.override') except OSError as error: if error.errno != errno.ENOENT: raise try: shutil.rmtree('/tmp/oneconf-test') except OSError as error: if error.errno != errno.ENOENT: raise atexit.register(cleanup) shutil.copy( os.path.join(os.path.dirname(__file__), "data", "oneconf.override"), '/tmp/oneconf.override') from oneconf import paths from oneconf.enums import MIN_TIME_WITHOUT_ACTIVITY class DaemonTests(unittest.TestCase): def setUp(self): self.dbus_service_process = None self.hostid = "0000" self.hostname = "foomachine" os.environ["ONECONF_HOST"] = "%s:%s" % (self.hostid, self.hostname) self.dbus_service_process = subprocess.Popen( ["./oneconf-service", '--debug', '--mock']) self.time_start = time.time() time.sleep(1) # let the main daemon starting def tearDown(self): '''Kill the dbus service if there, and clean things''' if self.dbus_service_process: self.dbus_service_process.terminate() self.dbus_service_process.wait() try: shutil.rmtree(os.path.dirname(paths.ONECONF_CACHE_DIR)) except OSError: pass def daemon_still_there(self, pid=None): '''Return True if the daemon is still running''' if not pid and self.dbus_service_process: pid = self.dbus_service_process.pid if pid: for line in os.popen("ps xa"): fields = line.split() if (str(pid) == fields[0]): return True return False def test_daemon_stop(self): '''Test that the daemon effectively stops when requested''' self.assertTrue(self.daemon_still_there()) subprocess.Popen(["./oneconf-query", "--stop"]) self.dbus_service_process.wait() # let it proceeding quitting time_stop = time.time() self.assertFalse(self.daemon_still_there()) self.assertTrue(time_stop - self.time_start < MIN_TIME_WITHOUT_ACTIVITY) self.dbus_service_process = None def test_unique_daemon(self): '''Try to spaw a second daemon and check it can't be there''' try: close = False try: devnull = subprocess.DEVNULL except AttributeError: # Python 2 devnull = open(os.devnull, 'wb') close = True daemon2 = subprocess.Popen(["./oneconf-service"], stdout=devnull, stderr=devnull) daemon2.wait() # let it proceeding quitting #time_stop = time.time() self.assertFalse(self.daemon_still_there(daemon2.pid)) # This assertion is unreliable, especially under DEP 8 # autopkgtests and possibly other limited environments. ## self.assertLess(time_stop - self.time_start, ## MIN_TIME_WITHOUT_ACTIVITY) finally: if close: devnull.close() def test_daemon_stop_after_timeout(self): '''Test that the daemon effectively stops after a timeout''' self.assertTrue(self.daemon_still_there()) self.dbus_service_process.wait() # let it proceeding quitting time_stop = time.time() self.assertFalse(self.daemon_still_there()) self.assertTrue(time_stop - self.time_start > MIN_TIME_WITHOUT_ACTIVITY) self.dbus_service_process = None def test_daemon_keep_alive_if_activity(self): '''Test that the daemon is kept alive if you have activity''' self.assertTrue(self.daemon_still_there()) subprocess.Popen(["./oneconf-query", '--host']) time.sleep(MIN_TIME_WITHOUT_ACTIVITY + 2) subprocess.Popen(["./oneconf-query", '--host']) self.dbus_service_process.wait() # let it proceeding quitting time_stop = time.time() self.assertFalse(self.daemon_still_there()) self.assertTrue(time_stop - self.time_start > 2*MIN_TIME_WITHOUT_ACTIVITY) self.dbus_service_process = None def test_no_daemon_crash_if_invalid_setup(self): '''Test that the daemon doesn't crash in case of invalid setup''' shutil.copy(os.path.join(os.path.dirname(__file__), "data", "oneconf.invaliddistro.override"), "/tmp/oneconf.override") self.assertTrue(self.daemon_still_there()) self.dbus_service_process.terminate() self.dbus_service_process.wait() # let the existing daemon quitting self.assertFalse(self.daemon_still_there()) self.dbus_service_process = None self.dbus_service_process = subprocess.Popen(["./oneconf-service", '--debug', '--mock']) self.assertTrue(self.daemon_still_there()) from oneconf import dbusconnect oneconf = dbusconnect.DbusConnect() oneconf.get_packages(self.hostid, '', False) oneconf.update() # try to wait for it syncing (for additional invalid data loading) for i in range(5): oneconf.get_all_hosts() time.sleep(MIN_TIME_WITHOUT_ACTIVITY) subprocess.Popen(["./oneconf-query", "--stop"]) # # main # if __name__ == '__main__': print(''' ######################################### # Use the OneConf service # ######################################### ''') unittest.main(exit=False) os.remove("/tmp/oneconf.override") oneconf-0.3.9/test/__init__.py0000664000000000000000000000000012624560235013125 0ustar oneconf-0.3.9/test/data/0000775000000000000000000000000012624561776011752 5ustar oneconf-0.3.9/test/data/oneconf.override0000664000000000000000000000034112624560235015125 0ustar [TestSuite] ONECONF_CACHE_DIR=/tmp/oneconf-test/cache WEBCATALOG_SILO_DIR=/tmp/oneconf-test/resulting_silo FAKE_WALLPAPER=test/data/wallpaper.png FAKE_WALLPAPER_MTIME=0000000000.000042 MIN_TIME_WITHOUT_ACTIVITY=5 distro=Test oneconf-0.3.9/test/data/integrationdatatests/0000775000000000000000000000000012624561776016212 5ustar oneconf-0.3.9/test/data/integrationdatatests/host_brokenlatestsync/0000775000000000000000000000000012624561776022641 5ustar oneconf-0.3.9/test/data/integrationdatatests/host_brokenlatestsync/host0000664000000000000000000000027712624560235023534 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": null, "share_inventory": false}oneconf-0.3.9/test/data/integrationdatatests/host_brokenlatestsync/last_sync0000664000000000000000000000041412624560235024547 0ustar ~DET˪#Ua^RA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRAoneconf-0.3.9/test/data/integrationdatatests/host_brokenhostfile/0000775000000000000000000000000012624561776022265 5ustar oneconf-0.3.9/test/data/integrationdatatests/host_brokenhostfile/host0000664000000000000000000000041412624560235023151 0ustar ~DET˪#Ua^RA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRAoneconf-0.3.9/test/data/integrationdatatests/host_brokenpending/0000775000000000000000000000000012624561776022074 5ustar oneconf-0.3.9/test/data/integrationdatatests/host_brokenpending/host0000664000000000000000000000027712624560235022767 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": null, "share_inventory": false}oneconf-0.3.9/test/data/integrationdatatests/host_brokenpending/pending_upload0000664000000000000000000000041412624560235024773 0ustar ~DET˪#Ua^RA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRAoneconf-0.3.9/test/data/integrationdatatests/host_brokenotherhosts/0000775000000000000000000000000012624561776022652 5ustar oneconf-0.3.9/test/data/integrationdatatests/host_brokenotherhosts/host0000664000000000000000000000027712624560235023545 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": null, "share_inventory": false}oneconf-0.3.9/test/data/integrationdatatests/host_brokenotherhosts/other_hosts0000664000000000000000000000041412624560235025122 0ustar ~DET˪#Ua^RA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRAoneconf-0.3.9/test/data/integrationdatatests/host_brokenpackageset/0000775000000000000000000000000012624561776022557 5ustar oneconf-0.3.9/test/data/integrationdatatests/host_brokenpackageset/package_list_00000000664000000000000000000023142412624560235025562 0ustar j(?/!ȅv1pSX:xy/YTAc k:' Y\lE!Z/ ]8 -Mܚ>o& 0L3*hts^$]&!)9>o~ؔ $.O{ Iנ,a@ػE|2b9bD-.DGvEx=hwHlq$u*Fs3s"K$mEC'lRa:%94ԍ)gg}-J;ΞVjvT2}?E5=!񑘂ݴ+XFC˲$}/s:Ź(-Ljl&NڐuswT- &L.$yz*Ot@sTrÞMaJE^`mB>RO?;-xeFKz?\>Ȃɨ<\b;0NͰ~C>ç=Aj D[ܺVQuUE)/4y&*"""+_T"xڧGe<7zַ"ޖa#ߩVFLn'7sĹCR@K5H=I} ZPv$/BHɠ g[t3_S7TMe̮wD")zZA y!ؓcz.DS_vcFا%b_MP;O? 9pY&@!muN9bT#LLCUy;uF;Z4Jg_2[ %bқ@iGaiZ$/IHj[d7野5?raL$]$\>W?K; fgD%Q\a3qQ8ܬeN`09mw8B6Rgey"ˑeWEU=OF^¿Qfخ nϵD ›V[ON߂VʅNvFР]O%ub*'ֿ.a7j, ؔ!RRXYHl~w!0W͏8qIѬ-"΄ɏW_4; z 8tҁ9ΫYpo Z5-]rאؙMP(?ks#3T`7?.(q[\X...^`ɩ!ms<2倗O''l~PC0`Ǥ\lcGY^_"=ﱈ{ij/|%= xO]UdjDVI㺈]3mrqЪ{\^OO"%5{/FU xR%an?U^ c 5O\U*lϑܺ Z(cUl˻ͭލbd#fNKYWL Jl!Yvljv&6|a`>p%*3 S$ԥJ I!+*Wx-uix1lֵb[8DE"L[>]l?m|20VƉ͝w$У^uK CՖM(F>i AOx;nx_2b#PB?ʦM n] !al#^Z-#&(#Ok^BMlb !#51r;^諓 8ya507*kO8 t!+/yZrT,fd/f+A^ŽޒAOuQtܮsv#G(RU$Eojde񖎉k#y|\tciik!t|ã82dU X65PR(1L;VO#$'y*Y*>v KVxF,Ivp|aX3՛ړ_la)L;ouܤ;MvbXkK=l2-Aŧ!.戕A.W R3_Ð_^:Xt+ި$Hq@ p bCPmLgRb}#Qzu$jq@W$Ӈ.U =\j\RV;W d.&7pB'`5#Yn>IJn[<6Vw $E:W2*i<1t{5D8;t9~%νBޗ&W|1.͈xPeAE w1Ʒy 8^LYv3Xd|GV">IUCfde);nE4ϊ$M0PVk'd>^}-Azz)<^ xI]9 A.)ep}Pf |OV?E4$9==@^tYKȿ=,5 MyL\a*uFlgZR"Q?P ÛIZ'\fa9gH>0ټ5ǔp {qXTƱy Wݐ1{L(ZXrtȥc@dZNueGqQԕRo6= zMvSOC5LSSFS> 9D1)G8Ty[0Q(^eN\CQF[+1oYo̖kFkB` "zq@ '߸ EfR/WA?8h{5?zR~9K:sѓ֪# EfR/WA?8h{ C Ј e(-Sȁt EfR/WA?8h{/nhܢ{WnA7I.".g=]|O!vyfRŨKA?8h{{ptK˱cЛ4[Zz-i]+|O!vyfRŨKA?8h{Do'0qd.;)=cw@!X_a|0lfRŨKaQ\8h{]C%W-rgsX xU^ A!X_a|0lfRŨKaQ\8h{`udDNs3oeh)wa]t/e X_a|0lfRŨKaQ\8h{D6-i^+cAZ;JF!X_a|0lfRŨKaQ\8h{ϭm/ⓟKʹHL8bd]PKŁVhgA!X_a|0lfRŨKaQ\8h{ [; ;TkfI_M,jh΍褚G!X_a|0lfRŨKaQ\8h{REўF>g5 X_a|0lfRŨKaQ\8h{5kGqב6=8\]W!L5 X_a|0lfRŨKaQ\8h{{~M' X_a|0lfRŨKaQ\8h{)DvJKoÃS MZ4 X_a|0lfRŨKaQ\8h{L7l_$ 7DyvckIJS X_a|0lfRŨKaQ\8h{ڞ6<[~.6cxM?! X_a|0lfRŨKaQ\8h{{ &$lcAx,Xo1K 9=oAJm X_a|0lfRŨKaQ\8h{mkNr h`'ŅÛdQ@n3 X_a|0lfRŨKaQ\8h{gɌ1x6QAYi.+~!K) X_a|0lfRŨKaQ\8h{;xvS@}J'M`Uۿ0WpMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uI0H ZkMe:luQKuMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uSNF׏VZkMe:luQKuMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_u_TD͎ZkMe:luQKuMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uܪѺݿ>nZkMe:luQKuMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_u!xQW""ZkMe:luQKuMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uAGOϠ=K4ZkMe:luQKuMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uJ D-mSZkMe:luQKuMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uvwrZkMe:luQKuMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_u}Z[ebh-zOZkMe:luQKuMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uD؞qsl+1~ZkMe:luQKuMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_u3q&x^ddΏZkMe:luQKuMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_u -'>\UR ZkMe:luQKuMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_u8evb6FFZkMe:luQKuMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_u[4ׂ\"0hZkMe:luQKuMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_ul- vZkMe:luQKuMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uw[02LVtZkMe:luQKuMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uքU)@gT@ZkMe:luQKuMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_u Ta1|ZkMe:luQKuMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6U_uMb6Uoneconf-0.3.9/test/data/integrationdatatests/host_brokenpackageset/host0000664000000000000000000000030012624560235023435 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": aaaa, "share_inventory": false} oneconf-0.3.9/test/data/hostdata/0000775000000000000000000000000012624561776013561 5ustar oneconf-0.3.9/test/data/hostdata/package_list_00000000664000000000000000000000011012624560235016546 0ustar {"foo": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": false}} oneconf-0.3.9/test/data/hostdata/host0000664000000000000000000000036012624560235014445 0ustar {"hostid": "0000", "logo_checksum": "c7e18f80419ea665772fef10e347f244d5ba596cc2764a8e611603061322543811.298964", "hostname": "tidus", "packages_checksum": "9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b", "share_inventory": true} oneconf-0.3.9/test/data/hostdata/last_sync0000664000000000000000000000003612624560235015467 0ustar {"last_sync": "123456789.00"} oneconf-0.3.9/test/data/hostdata/other_hosts0000664000000000000000000000042712624560235016035 0ustar {"AAAAAA": {"hostname": "julie-laptop", "logo_checksum": "a", "packages_checksum": "086e176b91b97f4b7ee30f583a69f65c5c237133f225cfe3e0adb4d3"}, "BBBBBB": {"hostname": "yuna", "logo_checksum": "b", "packages_checksum": "b9b81f11af3da6ade72132968fc590836874e161d1be9b064d4e3c1a"}} oneconf-0.3.9/test/data/hostdata/package_list_AAAAAA0000664000000000000000000000012112624560235017056 0ustar {"libqtdee2": {"auto": true}, "ttf-lao": {"auto": false}, "foo": {"auto": true}} oneconf-0.3.9/test/data/hostdata/logo_0000.png0000664000000000000000000000354012624560235015655 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/README0000664000000000000000000000032012624560235012612 0ustar The override file is used to parametize where the data are stored hostdata are is used for the static data tests syncdatatests are the data for both the silo and local current host state for the sync tests oneconf-0.3.9/test/data/oneconf.invaliddistro.override0000664000000000000000000000035112624560235020000 0ustar [TestSuite] ONECONF_CACHE_DIR=/tmp/oneconf-test/cache WEBCATALOG_SILO_DIR=/tmp/oneconf-test/resulting_silo FAKE_WALLPAPER=test/data/wallpaper.png FAKE_WALLPAPER_MTIME=0000000000.000042 MIN_TIME_WITHOUT_ACTIVITY=5 distro=foobardistro oneconf-0.3.9/test/data/syncdatatests/0000775000000000000000000000000012624561776014643 5ustar oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_remove_other_host/0000775000000000000000000000000012624561776023726 5ustar oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_remove_other_host/package_list_00000000664000000000000000000000011012624560235026713 0ustar {"foo": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": false}} oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_remove_other_host/host0000664000000000000000000000036512624560235024617 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b", "share_inventory": true} oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_remove_other_host/last_sync0000664000000000000000000000003612624560235025634 0ustar {"last_sync": "1325678295.16"}oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_remove_other_host/other_hosts0000664000000000000000000000013212624560235026173 0ustar {"AAAA": {"hostname": "aaaaa", "logo_checksum": null, "packages_checksum": "packageAAAA"}}oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_remove_other_host/logo_0000.png0000664000000000000000000000354012624560235026022 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_remove_other_host/package_list_AAAA0000664000000000000000000000015012624560235027023 0ustar {"libFool": {"auto": true}, "kiki": {"auto": false}, "unity": {"auto": false}, "libFoo": {"auto": true}}oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_updated_packages/0000775000000000000000000000000012624561776026750 5ustar ././@LongLink0000644000000000000000000000015100000000000011600 Lustar rootrootoneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_updated_packages/package_list_0000oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_updated_packages/package_list_0000664000000000000000000000011012624560235031435 0ustar {"foo": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": false}} oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_updated_packages/host0000664000000000000000000000036512624560235027641 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b", "share_inventory": true} oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_updated_packages/last_sync0000664000000000000000000000003612624560235030656 0ustar {"last_sync": "1325675472.16"}oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_updated_packages/other_hosts0000664000000000000000000000014112624560235031215 0ustar {"AAAA": {"hostname": "aaaaa", "logo_checksum": null, "packages_checksum": "updatedpackageAAAA"}}oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_updated_packages/logo_0000.png0000664000000000000000000000354012624560235031044 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`././@LongLink0000644000000000000000000000015100000000000011600 Lustar rootrootoneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_updated_packages/package_list_AAAAoneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_updated_packages/package_list_0000664000000000000000000000011712624560235031444 0ustar {"libFool": {"auto": true}, "unity": {"auto": false}, "libFoo": {"auto": true}}oneconf-0.3.9/test/data/syncdatatests/host_broken_pending_file/0000775000000000000000000000000012624561776021663 5ustar oneconf-0.3.9/test/data/syncdatatests/host_broken_pending_file/host0000664000000000000000000000027712624560235022556 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": null, "share_inventory": false}oneconf-0.3.9/test/data/syncdatatests/host_broken_pending_file/pending_upload0000664000000000000000000000041412624560235024562 0ustar ~DET˪#Ua^RA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRA٦߅hRAoneconf-0.3.9/test/data/syncdatatests/silo_sync_other_host_with_updated_hostname0000664000000000000000000000132412624560235025452 0ustar (dp1 S'delete_machine_error' p2 I00 sS'fake_network_delay' p3 I2 sS'update_packages_error' p4 I00 sS'list_packages_error' p5 I00 sS'list_machines_error' p6 I00 sS'server_response_error' p7 I00 sS'update_machine_error' p8 I00 sS'get_machine_logo_error' p9 I00 sS'hosts_metadata' p10 (dp11 S'AAAA' p12 (dp13 S'hostname' p14 S'aaaab' p15 sS'logo_checksum' p16 NsS'packages_checksum' p17 NssV0000 p18 (dp19 S'hostname' p20 Vfoomachine p21 sS'logo_checksum' p22 NsS'packages_checksum' p23 V9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b p24 sssS'update_machine_logo_error' p25 I00 sS'packages_metadata' p26 (dp27 g18 (dp28 Vbaz p29 (dp30 Vauto p31 I00 ssVfoo p32 (dp33 Vauto p34 I00 ssVbar p35 (dp36 Vauto p37 I01 ssss.oneconf-0.3.9/test/data/syncdatatests/silo_previously_shared_with_packages_notshared0000664000000000000000000000116512624560235026313 0ustar (dp1 S'delete_machine_error' p2 I00 sS'fake_network_delay' p3 I2 sS'update_packages_error' p4 I00 sS'list_packages_error' p5 I00 sS'list_machines_error' p6 I00 sS'server_response_error' p7 I00 sS'update_machine_error' p8 I00 sS'get_machine_logo_error' p9 I00 sS'hosts_metadata' p10 (dp11 S'0000' p12 (dp13 S'hostname' p14 S'foomachine' p15 sS'logo_checksum' p16 NsS'packages_checksum' p17 V9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b p18 sssS'update_machine_logo_error' p19 I00 sS'packages_metadata' p20 (dp21 g12 (dp22 Vbaz p23 (dp24 Vauto p25 I00 ssVfoo p26 (dp27 Vauto p28 I00 ssVbar p29 (dp30 Vauto p31 I01 ssss.oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_a_newhost_with_already_other_hosts/0000775000000000000000000000000012624561776027337 5ustar ././@LongLink0000644000000000000000000000015300000000000011602 Lustar rootrootoneconf-0.3.9/test/data/syncdatatests/resulthost_sync_a_newhost_with_already_other_hosts/package_list_0000oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_a_newhost_with_already_other_hosts/package_lis0000664000000000000000000000011012624560235031501 0ustar {"foo": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": false}} oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_a_newhost_with_already_other_hosts/host0000664000000000000000000000036512624560235030230 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b", "share_inventory": true} oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_a_newhost_with_already_other_hosts/last_sync0000664000000000000000000000003612624560235031245 0ustar {"last_sync": "1325676636.16"}././@LongLink0000644000000000000000000000015300000000000011602 Lustar rootrootoneconf-0.3.9/test/data/syncdatatests/resulthost_sync_a_newhost_with_already_other_hosts/package_list_BBBBoneconf-0.3.9/test/data/syncdatatests/resulthost_sync_a_newhost_with_already_other_hosts/package_lis0000664000000000000000000000003312624560235031505 0ustar {"libFool": {"auto": true}}oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_a_newhost_with_already_other_hosts/other_hosts0000664000000000000000000000027312624560235031612 0ustar {"AAAA": {"hostname": "aaaaa", "logo_checksum": null, "packages_checksum": "updatedpackageAAAA"}, "BBBB": {"hostname": "bbbbb", "logo_checksum": null, "packages_checksum": "packageBBBB"}}././@LongLink0000644000000000000000000000014700000000000011605 Lustar rootrootoneconf-0.3.9/test/data/syncdatatests/resulthost_sync_a_newhost_with_already_other_hosts/logo_0000.pngoneconf-0.3.9/test/data/syncdatatests/resulthost_sync_a_newhost_with_already_other_hosts/logo_0000.p0000664000000000000000000000354012624560235031106 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`././@LongLink0000644000000000000000000000015300000000000011602 Lustar rootrootoneconf-0.3.9/test/data/syncdatatests/resulthost_sync_a_newhost_with_already_other_hosts/package_list_AAAAoneconf-0.3.9/test/data/syncdatatests/resulthost_sync_a_newhost_with_already_other_hosts/package_lis0000664000000000000000000000011712624560235031510 0ustar {"libFool": {"auto": true}, "unity": {"auto": false}, "libFoo": {"auto": true}}oneconf-0.3.9/test/data/syncdatatests/host_sync_other_host_with_updated_packages/0000775000000000000000000000000012624561776025511 5ustar oneconf-0.3.9/test/data/syncdatatests/host_sync_other_host_with_updated_packages/package_list_00000000664000000000000000000000011012624560235030476 0ustar {"foo": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": false}} oneconf-0.3.9/test/data/syncdatatests/host_sync_other_host_with_updated_packages/host0000664000000000000000000000036512624560235026402 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b", "share_inventory": true} oneconf-0.3.9/test/data/syncdatatests/host_sync_other_host_with_updated_packages/last_sync0000664000000000000000000000003612624560235027417 0ustar {"last_sync": "1325674771.16"}oneconf-0.3.9/test/data/syncdatatests/host_sync_other_host_with_updated_packages/other_hosts0000664000000000000000000000013212624560235027756 0ustar {"AAAA": {"hostname": "aaaaa", "logo_checksum": null, "packages_checksum": "packageAAAA"}}oneconf-0.3.9/test/data/syncdatatests/host_sync_other_host_with_updated_packages/logo_0000.png0000664000000000000000000000354012624560235027605 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/host_sync_other_host_with_updated_packages/package_list_AAAA0000664000000000000000000000015012624560235030606 0ustar {"libFool": {"auto": true}, "kiki": {"auto": false}, "unity": {"auto": false}, "libFoo": {"auto": true}}oneconf-0.3.9/test/data/syncdatatests/silo_sync_other_host_with_packages0000664000000000000000000000155712624560235023714 0ustar (dp1 S'delete_machine_error' p2 I00 sS'fake_network_delay' p3 I2 sS'update_packages_error' p4 I00 sS'list_packages_error' p5 I00 sS'list_machines_error' p6 I00 sS'server_response_error' p7 I00 sS'update_machine_error' p8 I00 sS'get_machine_logo_error' p9 I00 sS'hosts_metadata' p10 (dp11 S'AAAA' p12 (dp13 S'hostname' p14 S'aaaaa' p15 sS'logo_checksum' p16 NsS'packages_checksum' p17 S'packageAAAA' p18 ssV0000 p19 (dp20 S'hostname' p21 Vfoomachine p22 sS'logo_checksum' p23 NsS'packages_checksum' p24 V9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b p25 sssS'update_machine_logo_error' p26 I00 sS'packages_metadata' p27 (dp28 g12 (dp29 S'libFool' p30 (dp31 S'auto' p32 I01 ssS'unity' p33 (dp34 g32 I00 ssS'kiki' p35 (dp36 g32 I00 ssS'libFoo' p37 (dp38 g32 I01 sssg19 (dp39 Vbar p40 (dp41 Vauto p42 I01 ssVfoo p43 (dp44 Vauto p45 I00 ssVbaz p46 (dp47 Vauto p48 I00 ssss.oneconf-0.3.9/test/data/syncdatatests/silo_firsttime_sync_other_host0000664000000000000000000000124212624560235023100 0ustar (dp1 S'delete_machine_error' p2 I00 sS'fake_network_delay' p3 I2 sS'update_packages_error' p4 I00 sS'list_packages_error' p5 I00 sS'list_machines_error' p6 I00 sS'server_response_error' p7 I00 sS'update_machine_error' p8 I00 sS'get_machine_logo_error' p9 I00 sS'hosts_metadata' p10 (dp11 S'AAAA' p12 (dp13 S'hostname' p14 S'aaaaa' p15 sS'logo_checksum' p16 NssV0000 p17 (dp18 g14 Vfoomachine p19 sg16 NsS'packages_checksum' p20 V9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b p21 sssS'update_machine_logo_error' p22 I00 sS'packages_metadata' p23 (dp24 V0000 p25 (dp26 Vbaz p27 (dp28 Vauto p29 I00 ssVfoo p30 (dp31 Vauto p32 I00 ssVbar p33 (dp34 Vauto p35 I01 ssss.oneconf-0.3.9/test/data/syncdatatests/host_delete_current_host_error/0000775000000000000000000000000012624561776023152 5ustar oneconf-0.3.9/test/data/syncdatatests/host_delete_current_host_error/package_list_00000000664000000000000000000000011012624560235026137 0ustar {"foo": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": false}} oneconf-0.3.9/test/data/syncdatatests/host_delete_current_host_error/host0000664000000000000000000000030512624560235024035 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "newlist", "share_inventory": false} oneconf-0.3.9/test/data/syncdatatests/host_delete_current_host_error/last_sync0000664000000000000000000000003612624560235025060 0ustar {"last_sync": "1325674771.16"}oneconf-0.3.9/test/data/syncdatatests/host_delete_current_host_error/other_hosts0000664000000000000000000000013212624560235025417 0ustar {"AAAA": {"hostname": "aaaaa", "logo_checksum": null, "packages_checksum": "packageAAAA"}}oneconf-0.3.9/test/data/syncdatatests/host_delete_current_host_error/logo_0000.png0000664000000000000000000000354012624560235025246 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/host_delete_current_host_error/package_list_AAAA0000664000000000000000000000015012624560235026247 0ustar {"libFool": {"auto": true}, "kiki": {"auto": false}, "unity": {"auto": false}, "libFoo": {"auto": true}}oneconf-0.3.9/test/data/syncdatatests/host_with_packages/0000775000000000000000000000000012624561776020511 5ustar oneconf-0.3.9/test/data/syncdatatests/host_with_packages/package_list_00000000664000000000000000000000011012624560235023476 0ustar {"foo": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": false}} oneconf-0.3.9/test/data/syncdatatests/host_with_packages/host0000664000000000000000000000036512624560235021402 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b", "share_inventory": true} oneconf-0.3.9/test/data/syncdatatests/host_with_packages/logo_0000.png0000664000000000000000000000354012624560235022605 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/host_only_current_host/0000775000000000000000000000000012624561776021460 5ustar oneconf-0.3.9/test/data/syncdatatests/host_only_current_host/package_list_00000000664000000000000000000000011012624560235024445 0ustar {"foo": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": false}} oneconf-0.3.9/test/data/syncdatatests/host_only_current_host/host0000664000000000000000000000036512624560235022351 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b", "share_inventory": true} oneconf-0.3.9/test/data/syncdatatests/host_only_current_host/logo_0000.png0000664000000000000000000000354012624560235023554 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/host_unshare_other_host/0000775000000000000000000000000012624561776021603 5ustar oneconf-0.3.9/test/data/syncdatatests/host_unshare_other_host/host0000664000000000000000000000040712624560235022471 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "dab2fbc6250b6fea7a245e3eedb7bfdba7578d93bbc2df6c787d61271319017141.562973", "share_inventory": false} oneconf-0.3.9/test/data/syncdatatests/host_unshare_other_host/pending_upload0000664000000000000000000000004512624560235024502 0ustar {"AAAA": {"share_inventory": false}} oneconf-0.3.9/test/data/syncdatatests/host_unshare_other_host/logo_0000.png0000664000000000000000000000354012624560235023677 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/silo_sync_a_newhost_with_already_other_hosts0000664000000000000000000000157312624560235026007 0ustar (dp1 S'fake_network_delay' p2 I2 sS'server_response_error' p3 I00 sS'update_machine_error' p4 I00 sS'hosts_metadata' p5 (dp6 S'AAAA' p7 (dp8 S'hostname' p9 S'aaaaa' p10 sS'logo_checksum' p11 NsS'packages_checksum' p12 S'updatedpackageAAAA' p13 ssS'BBBB' p14 (dp15 g9 S'bbbbb' p16 sg11 Nsg12 S'packageBBBB' p17 ssV0000 p18 (dp19 g9 Vfoomachine p20 sg11 Nsg12 V9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b p21 sssS'delete_machine_error' p22 I00 sS'update_packages_error' p23 I00 sS'list_packages_error' p24 I00 sS'list_machines_error' p25 I00 sS'get_machine_logo_error' p26 I00 sS'update_machine_logo_error' p27 I00 sS'packages_metadata' p28 (dp29 g7 (dp30 S'libFool' p31 (dp32 S'auto' p33 I01 ssS'libFoo' p34 (dp35 g33 I01 ssS'unity' p36 (dp37 g33 I00 sssg14 (dp38 g31 (dp39 g33 I01 sssg18 (dp40 Vbaz p41 (dp42 Vauto p43 I00 ssVfoo p44 (dp45 g43 I00 ssVbar p46 (dp47 g43 I01 ssss.oneconf-0.3.9/test/data/syncdatatests/silo_fake_server_errors0000664000000000000000000000165012624560235021473 0ustar (dp1 S'delete_machine_error' p2 I00 sS'fake_network_delay' p3 I2 sS'update_packages_error' p4 I00 sS'list_packages_error' p5 I00 sS'list_machines_error' p6 I00 sS'server_response_error' p7 I00 sS'update_machine_error' p8 I00 sS'get_machine_logo_error' p9 I00 sS'hosts_metadata' p10 (dp11 S'AAAA' p12 (dp13 S'hostname' p14 S'aaaa' p15 sS'logo_checksum' p16 NsS'packages_checksum' p17 S'packageaaaa' p18 ssS'0000' p19 (dp20 S'hostname' p21 S'barmachine' p22 sS'logo_checksum' p23 NsS'packages_checksum' p24 V9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b p25 ssS'BBBB' p26 (dp27 g14 S'toremove' p28 sg16 Nsg17 Vtoremove p29 sssS'update_machine_logo_error' p30 I00 sS'packages_metadata' p31 (dp32 g12 (dp33 Vbar p34 (dp35 Vauto p36 I01 ssS'foo' p37 (dp38 g36 I00 ssS'baz' p39 (dp40 g36 I00 sssg19 (dp41 Vbaz p42 (dp43 Vauto p44 I00 ssVfoo p45 (dp46 g44 I00 ssVbar p47 (dp48 g44 I01 sssg26 (dp49 Vbar p50 (dp51 Vauto p52 I00 ssss.oneconf-0.3.9/test/data/syncdatatests/silo_delete_current_host_error0000664000000000000000000000165012624560235023055 0ustar (dp1 S'delete_machine_error' p2 I00 sS'fake_network_delay' p3 I2 sS'update_packages_error' p4 I00 sS'list_packages_error' p5 I00 sS'list_machines_error' p6 I00 sS'server_response_error' p7 I00 sS'update_machine_error' p8 I00 sS'get_machine_logo_error' p9 I00 sS'hosts_metadata' p10 (dp11 S'AAAA' p12 (dp13 S'hostname' p14 S'aaaa' p15 sS'logo_checksum' p16 NsS'packages_checksum' p17 S'packageaaaa' p18 ssS'0000' p19 (dp20 S'hostname' p21 S'barmachine' p22 sS'logo_checksum' p23 NsS'packages_checksum' p24 V9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b p25 ssS'BBBB' p26 (dp27 g14 S'toremove' p28 sg16 Nsg17 Vtoremove p29 sssS'update_machine_logo_error' p30 I00 sS'packages_metadata' p31 (dp32 g12 (dp33 Vbar p34 (dp35 Vauto p36 I01 ssS'foo' p37 (dp38 g36 I00 ssS'baz' p39 (dp40 g36 I00 sssg19 (dp41 Vbaz p42 (dp43 Vauto p44 I00 ssVfoo p45 (dp46 g44 I00 ssVbar p47 (dp48 g44 I01 sssg26 (dp49 Vbar p50 (dp51 Vauto p52 I00 ssss.oneconf-0.3.9/test/data/syncdatatests/silo_update_current_hostname0000664000000000000000000000116112624560235022522 0ustar (dp1 S'delete_machine_error' p2 I00 sS'fake_network_delay' p3 I2 sS'update_packages_error' p4 I00 sS'list_packages_error' p5 I00 sS'list_machines_error' p6 I00 sS'server_response_error' p7 I00 sS'update_machine_error' p8 I00 sS'get_machine_logo_error' p9 I00 sS'hosts_metadata' p10 (dp11 V0000 p12 (dp13 S'hostname' p14 Vfoomachine p15 sS'logo_checksum' p16 NsS'packages_checksum' p17 V9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b p18 sssS'update_machine_logo_error' p19 I00 sS'packages_metadata' p20 (dp21 g12 (dp22 Vbaz p23 (dp24 Vauto p25 I00 ssVfoo p26 (dp27 Vauto p28 I00 ssVbar p29 (dp30 Vauto p31 I01 ssss.oneconf-0.3.9/test/data/syncdatatests/host_update_current_hostname/0000775000000000000000000000000012624561776022622 5ustar oneconf-0.3.9/test/data/syncdatatests/host_update_current_hostname/package_list_00000000664000000000000000000000011012624560235025607 0ustar {"foo": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": false}} oneconf-0.3.9/test/data/syncdatatests/host_update_current_hostname/host0000664000000000000000000000036512624560235023513 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "barmachine", "packages_checksum": "9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b", "share_inventory": true} oneconf-0.3.9/test/data/syncdatatests/host_update_current_hostname/logo_0000.png0000664000000000000000000000354012624560235024716 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/host_nosilo_nopackage_onlyhost_noshare/0000775000000000000000000000000012624561776024671 5ustar oneconf-0.3.9/test/data/syncdatatests/host_nosilo_nopackage_onlyhost_noshare/host0000664000000000000000000000040712624560235025557 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "dab2fbc6250b6fea7a245e3eedb7bfdba7578d93bbc2df6c787d61271319017141.562973", "share_inventory": false} oneconf-0.3.9/test/data/syncdatatests/host_nosilo_nopackage_onlyhost_noshare/logo_0000.png0000664000000000000000000000354012624560235026765 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_packages/0000775000000000000000000000000012624561776025242 5ustar oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_packages/package_list_00000000664000000000000000000000011012624560235030227 0ustar {"foo": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": false}} oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_packages/host0000664000000000000000000000036512624560235026133 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b", "share_inventory": true} oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_packages/last_sync0000664000000000000000000000003612624560235027150 0ustar {"last_sync": "1325674771.16"}oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_packages/other_hosts0000664000000000000000000000013212624560235027507 0ustar {"AAAA": {"hostname": "aaaaa", "logo_checksum": null, "packages_checksum": "packageAAAA"}}oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_packages/logo_0000.png0000664000000000000000000000354012624560235027336 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_packages/package_list_AAAA0000664000000000000000000000015012624560235030337 0ustar {"libFool": {"auto": true}, "kiki": {"auto": false}, "unity": {"auto": false}, "libFoo": {"auto": true}}oneconf-0.3.9/test/data/syncdatatests/host_sync_other_host_with_packages/0000775000000000000000000000000012624561776024003 5ustar oneconf-0.3.9/test/data/syncdatatests/host_sync_other_host_with_packages/package_list_00000000664000000000000000000000011012624560235026770 0ustar {"foo": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": false}} oneconf-0.3.9/test/data/syncdatatests/host_sync_other_host_with_packages/host0000664000000000000000000000036512624560235024674 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b", "share_inventory": true} oneconf-0.3.9/test/data/syncdatatests/host_sync_other_host_with_packages/logo_0000.png0000664000000000000000000000354012624560235026077 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/host_nosilo_nopackage_onlyhost/0000775000000000000000000000000012624561776023152 5ustar oneconf-0.3.9/test/data/syncdatatests/host_nosilo_nopackage_onlyhost/host0000664000000000000000000000027712624560235024045 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": null, "share_inventory": true} oneconf-0.3.9/test/data/syncdatatests/host_nosilo_nopackage_onlyhost/logo_0000.png0000664000000000000000000000354012624560235025246 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/host_update_packages_for_current_host/0000775000000000000000000000000012624561776024465 5ustar oneconf-0.3.9/test/data/syncdatatests/host_update_packages_for_current_host/package_list_00000000664000000000000000000000010712624560235027460 0ustar {"fol": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": true}} oneconf-0.3.9/test/data/syncdatatests/host_update_packages_for_current_host/host0000664000000000000000000000030112624560235025344 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "AAAA", "share_inventory": true} oneconf-0.3.9/test/data/syncdatatests/host_update_packages_for_current_host/logo_0000.png0000664000000000000000000000354012624560235026561 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/resulthost_firsttime_sync_other_host/0000775000000000000000000000000012624561776024437 5ustar oneconf-0.3.9/test/data/syncdatatests/resulthost_firsttime_sync_other_host/package_list_00000000664000000000000000000000011012624560235027424 0ustar {"foo": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": false}} oneconf-0.3.9/test/data/syncdatatests/resulthost_firsttime_sync_other_host/host0000664000000000000000000000036512624560235025330 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b", "share_inventory": true} oneconf-0.3.9/test/data/syncdatatests/resulthost_firsttime_sync_other_host/last_sync0000664000000000000000000000003612624560235026345 0ustar {"last_sync": "1325673466.16"}oneconf-0.3.9/test/data/syncdatatests/resulthost_firsttime_sync_other_host/other_hosts0000664000000000000000000000012112624560235026702 0ustar {"AAAA": {"hostname": "aaaaa", "logo_checksum": null, "packages_checksum": null}}oneconf-0.3.9/test/data/syncdatatests/resulthost_firsttime_sync_other_host/logo_0000.png0000664000000000000000000000354012624560235026533 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/host_previously_shared_notshared/0000775000000000000000000000000012624561776023516 5ustar oneconf-0.3.9/test/data/syncdatatests/host_previously_shared_notshared/host0000664000000000000000000000040712624560235024404 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "dab2fbc6250b6fea7a245e3eedb7bfdba7578d93bbc2df6c787d61271319017141.562973", "share_inventory": false} oneconf-0.3.9/test/data/syncdatatests/host_previously_shared_notshared/logo_0000.png0000664000000000000000000000354012624560235025612 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/silo_previously_shared_notshared0000664000000000000000000000071412624560235023421 0ustar (dp1 S'delete_machine_error' p2 I00 sS'fake_network_delay' p3 I2 sS'update_packages_error' p4 I00 sS'list_packages_error' p5 I00 sS'list_machines_error' p6 I00 sS'server_response_error' p7 I00 sS'update_machine_error' p8 I00 sS'get_machine_logo_error' p9 I00 sS'hosts_metadata' p10 (dp11 V0000 p12 (dp13 S'hostname' p14 Vfoomachine p15 sS'logo_checksum' p16 NsS'packages_checksum' p17 NsssS'update_machine_logo_error' p18 I00 sS'packages_metadata' p19 (dp20 s.oneconf-0.3.9/test/data/syncdatatests/silo_only_current_host0000664000000000000000000000116112624560235021360 0ustar (dp1 S'delete_machine_error' p2 I00 sS'fake_network_delay' p3 I2 sS'update_packages_error' p4 I00 sS'list_packages_error' p5 I00 sS'list_machines_error' p6 I00 sS'server_response_error' p7 I00 sS'update_machine_error' p8 I00 sS'get_machine_logo_error' p9 I00 sS'hosts_metadata' p10 (dp11 V0000 p12 (dp13 S'hostname' p14 Vfoomachine p15 sS'logo_checksum' p16 NsS'packages_checksum' p17 V9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b p18 sssS'update_machine_logo_error' p19 I00 sS'packages_metadata' p20 (dp21 g12 (dp22 Vbaz p23 (dp24 Vauto p25 I00 ssVfoo p26 (dp27 Vauto p28 I00 ssVbar p29 (dp30 Vauto p31 I01 ssss.oneconf-0.3.9/test/data/syncdatatests/silo_sync_other_host_with_updated_packages0000664000000000000000000000153212624560235025413 0ustar (dp1 S'delete_machine_error' p2 I00 sS'fake_network_delay' p3 I2 sS'update_packages_error' p4 I00 sS'list_packages_error' p5 I00 sS'list_machines_error' p6 I00 sS'server_response_error' p7 I00 sS'update_machine_error' p8 I00 sS'get_machine_logo_error' p9 I00 sS'hosts_metadata' p10 (dp11 S'AAAA' p12 (dp13 S'hostname' p14 S'aaaaa' p15 sS'logo_checksum' p16 NsS'packages_checksum' p17 S'updatedpackageAAAA' p18 ssV0000 p19 (dp20 S'hostname' p21 Vfoomachine p22 sS'logo_checksum' p23 NsS'packages_checksum' p24 V9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b p25 sssS'update_machine_logo_error' p26 I00 sS'packages_metadata' p27 (dp28 g12 (dp29 S'libFool' p30 (dp31 S'auto' p32 I01 ssS'unity' p33 (dp34 g32 I00 ssS'libFoo' p35 (dp36 g32 I01 sssg19 (dp37 Vbar p38 (dp39 Vauto p40 I01 ssVfoo p41 (dp42 Vauto p43 I00 ssVbaz p44 (dp45 Vauto p46 I00 ssss.oneconf-0.3.9/test/data/syncdatatests/silo_sync_remove_other_host0000664000000000000000000000155712624560235022400 0ustar (dp1 S'delete_machine_error' p2 I00 sS'fake_network_delay' p3 I2 sS'update_packages_error' p4 I00 sS'list_packages_error' p5 I00 sS'list_machines_error' p6 I00 sS'server_response_error' p7 I00 sS'update_machine_error' p8 I00 sS'get_machine_logo_error' p9 I00 sS'hosts_metadata' p10 (dp11 S'AAAA' p12 (dp13 S'hostname' p14 S'aaaaa' p15 sS'logo_checksum' p16 NsS'packages_checksum' p17 S'packageAAAA' p18 ssV0000 p19 (dp20 S'hostname' p21 Vfoomachine p22 sS'logo_checksum' p23 NsS'packages_checksum' p24 V9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b p25 sssS'update_machine_logo_error' p26 I00 sS'packages_metadata' p27 (dp28 g12 (dp29 S'libFool' p30 (dp31 S'auto' p32 I01 ssS'unity' p33 (dp34 g32 I00 ssS'kiki' p35 (dp36 g32 I00 ssS'libFoo' p37 (dp38 g32 I01 sssg19 (dp39 Vbar p40 (dp41 Vauto p42 I01 ssVfoo p43 (dp44 Vauto p45 I00 ssVbaz p46 (dp47 Vauto p48 I00 ssss.oneconf-0.3.9/test/data/syncdatatests/host_sync_a_newhost_with_already_other_hosts/0000775000000000000000000000000012624561776026100 5ustar oneconf-0.3.9/test/data/syncdatatests/host_sync_a_newhost_with_already_other_hosts/package_list_00000000664000000000000000000000011012624560235031065 0ustar {"foo": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": false}} oneconf-0.3.9/test/data/syncdatatests/host_sync_a_newhost_with_already_other_hosts/host0000664000000000000000000000036512624560235026771 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b", "share_inventory": true} oneconf-0.3.9/test/data/syncdatatests/host_sync_a_newhost_with_already_other_hosts/last_sync0000664000000000000000000000003612624560235030006 0ustar {"last_sync": "1325675472.16"}oneconf-0.3.9/test/data/syncdatatests/host_sync_a_newhost_with_already_other_hosts/other_hosts0000664000000000000000000000014112624560235030345 0ustar {"AAAA": {"hostname": "aaaaa", "logo_checksum": null, "packages_checksum": "updatedpackageAAAA"}}oneconf-0.3.9/test/data/syncdatatests/host_sync_a_newhost_with_already_other_hosts/logo_0000.png0000664000000000000000000000354012624560235030174 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/host_sync_a_newhost_with_already_other_hosts/package_list_AAAA0000664000000000000000000000011712624560235031200 0ustar {"libFool": {"auto": true}, "unity": {"auto": false}, "libFoo": {"auto": true}}oneconf-0.3.9/test/data/syncdatatests/host_fake_server_errors/0000775000000000000000000000000012624561776021570 5ustar oneconf-0.3.9/test/data/syncdatatests/host_fake_server_errors/package_list_00000000664000000000000000000000011012624560235024555 0ustar {"foo": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": false}} oneconf-0.3.9/test/data/syncdatatests/host_fake_server_errors/host0000664000000000000000000000030412624560235022452 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "newlist", "share_inventory": true} oneconf-0.3.9/test/data/syncdatatests/host_fake_server_errors/last_sync0000664000000000000000000000003612624560235023476 0ustar {"last_sync": "1325674771.16"}oneconf-0.3.9/test/data/syncdatatests/host_fake_server_errors/other_hosts0000664000000000000000000000013212624560235024035 0ustar {"AAAA": {"hostname": "aaaaa", "logo_checksum": null, "packages_checksum": "packageAAAA"}}oneconf-0.3.9/test/data/syncdatatests/host_fake_server_errors/pending_upload0000664000000000000000000000004512624560235024467 0ustar {"BBBB": {"share_inventory": false}} oneconf-0.3.9/test/data/syncdatatests/host_fake_server_errors/logo_0000.png0000664000000000000000000000354012624560235023664 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/host_fake_server_errors/package_list_AAAA0000664000000000000000000000015012624560235024665 0ustar {"libFool": {"auto": true}, "kiki": {"auto": false}, "unity": {"auto": false}, "libFoo": {"auto": true}}oneconf-0.3.9/test/data/syncdatatests/host_firsttime_sync_other_host/0000775000000000000000000000000012624561776023200 5ustar oneconf-0.3.9/test/data/syncdatatests/host_firsttime_sync_other_host/package_list_00000000664000000000000000000000011012624560235026165 0ustar {"foo": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": false}} oneconf-0.3.9/test/data/syncdatatests/host_firsttime_sync_other_host/host0000664000000000000000000000036512624560235024071 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b", "share_inventory": true} oneconf-0.3.9/test/data/syncdatatests/host_firsttime_sync_other_host/logo_0000.png0000664000000000000000000000354012624560235025274 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/silo_update_packages_for_current_host0000664000000000000000000000116112624560235024365 0ustar (dp1 S'delete_machine_error' p2 I00 sS'fake_network_delay' p3 I2 sS'update_packages_error' p4 I00 sS'list_packages_error' p5 I00 sS'list_machines_error' p6 I00 sS'server_response_error' p7 I00 sS'update_machine_error' p8 I00 sS'get_machine_logo_error' p9 I00 sS'hosts_metadata' p10 (dp11 V0000 p12 (dp13 S'hostname' p14 Vfoomachine p15 sS'logo_checksum' p16 NsS'packages_checksum' p17 V9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b p18 sssS'update_machine_logo_error' p19 I00 sS'packages_metadata' p20 (dp21 g12 (dp22 Vbaz p23 (dp24 Vauto p25 I00 ssVfoo p26 (dp27 Vauto p28 I00 ssVbar p29 (dp30 Vauto p31 I01 ssss.oneconf-0.3.9/test/data/syncdatatests/host_previously_shared_with_packages_notshared/0000775000000000000000000000000012624561776026407 5ustar ././@LongLink0000644000000000000000000000014700000000000011605 Lustar rootrootoneconf-0.3.9/test/data/syncdatatests/host_previously_shared_with_packages_notshared/package_list_0000oneconf-0.3.9/test/data/syncdatatests/host_previously_shared_with_packages_notshared/package_list_000000664000000000000000000000011012624560235031234 0ustar {"foo": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": false}} oneconf-0.3.9/test/data/syncdatatests/host_previously_shared_with_packages_notshared/host0000664000000000000000000000036612624560235027301 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b", "share_inventory": false} oneconf-0.3.9/test/data/syncdatatests/host_previously_shared_with_packages_notshared/logo_0000.png0000664000000000000000000000354012624560235030503 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_updated_hostname/0000775000000000000000000000000012624561776027010 5ustar ././@LongLink0000644000000000000000000000015100000000000011600 Lustar rootrootoneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_updated_hostname/package_list_0000oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_updated_hostname/package_list_0000664000000000000000000000011012624560235031475 0ustar {"foo": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": false}} oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_updated_hostname/host0000664000000000000000000000036512624560235027701 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b", "share_inventory": true} oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_updated_hostname/last_sync0000664000000000000000000000003612624560235030716 0ustar {"last_sync": "1325673466.16"}oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_updated_hostname/other_hosts0000664000000000000000000000012112624560235031253 0ustar {"AAAA": {"hostname": "aaaab", "logo_checksum": null, "packages_checksum": null}}oneconf-0.3.9/test/data/syncdatatests/resulthost_sync_other_host_with_updated_hostname/logo_0000.png0000664000000000000000000000354012624560235031104 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/host_sync_other_host_with_updated_hostname/0000775000000000000000000000000012624561776025551 5ustar oneconf-0.3.9/test/data/syncdatatests/host_sync_other_host_with_updated_hostname/package_list_00000000664000000000000000000000011012624560235030536 0ustar {"foo": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": false}} oneconf-0.3.9/test/data/syncdatatests/host_sync_other_host_with_updated_hostname/host0000664000000000000000000000036512624560235026442 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b", "share_inventory": true} oneconf-0.3.9/test/data/syncdatatests/host_sync_other_host_with_updated_hostname/last_sync0000664000000000000000000000003612624560235027457 0ustar {"last_sync": "1325673466.16"}oneconf-0.3.9/test/data/syncdatatests/host_sync_other_host_with_updated_hostname/other_hosts0000664000000000000000000000012112624560235030014 0ustar {"AAAA": {"hostname": "aaaaa", "logo_checksum": null, "packages_checksum": null}}oneconf-0.3.9/test/data/syncdatatests/host_sync_other_host_with_updated_hostname/logo_0000.png0000664000000000000000000000354012624560235027645 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/host_sync_remove_other_host/0000775000000000000000000000000012624561776022467 5ustar oneconf-0.3.9/test/data/syncdatatests/host_sync_remove_other_host/package_list_00000000664000000000000000000000011012624560235025454 0ustar {"foo": {"auto": false}, "bar": {"auto": true}, "baz": {"auto": false}} oneconf-0.3.9/test/data/syncdatatests/host_sync_remove_other_host/host0000664000000000000000000000036512624560235023360 0ustar {"hostid": "0000", "logo_checksum": "da2ff07f545c6f3ba06629ccb35f42d8ba4055eedb2a8a4818e8a5ca0000000000.000042", "hostname": "foomachine", "packages_checksum": "9c0d4e619c445551541af522b39ab483ba943b8b298fb96ccc3acd0b", "share_inventory": true} oneconf-0.3.9/test/data/syncdatatests/host_sync_remove_other_host/last_sync0000664000000000000000000000003612624560235024375 0ustar {"last_sync": "1325676636.16"}oneconf-0.3.9/test/data/syncdatatests/host_sync_remove_other_host/package_list_BBBB0000664000000000000000000000003312624560235025570 0ustar {"libFool": {"auto": true}}oneconf-0.3.9/test/data/syncdatatests/host_sync_remove_other_host/other_hosts0000664000000000000000000000026512624560235024743 0ustar {"AAAA": {"hostname": "aaaaa", "logo_checksum": null, "packages_checksum": "packageAAAA"}, "BBBB": {"hostname": "bbbbb", "logo_checksum": null, "packages_checksum": "packageBBBB"}} oneconf-0.3.9/test/data/syncdatatests/host_sync_remove_other_host/logo_0000.png0000664000000000000000000000354012624560235024563 0ustar PNG  IHDR00W'IDATx}L?<Ͼz ^|G{x"M9߰b{w]ڦmդi/1ilkKT4.S^S=ѻ+E^|k, ,]paWESIM&3}70a*Ԙ@B蚦ʕ+UEQFLRUt:I)UUUqݏdhAUU @Jטp]& BNx L<;O:|& 4M 0 %&I<0QTnma]8H "N,  Lp]?}LaCs<ZJOӘdM"t'kwe\G(wij?f)!$󿲋 c؁pw'vA2bw,ٰ-ryH0p+ʟ{lAvHRceH# { ztؠf\ε 1E<2_zx GOF C&&fLի*2FX;ļ`:u=]]]^-[-mnnV/Y󌄄+n;f(OJ)TUu$''OH~Ϟ=lڴSTd8 y ]@u!8arrrx}'|чx\aqbZZlBDjjuޥ޾hݷ>Zkg6l]܁L2川_x޽vJKK),,AAnyձb =BffǏtNdҥ455uϦ(3ioo.tĄDV^ͯ30&>>nbcc߱c6=a*++?oX\唔`}2E((P`}o(EBkk+ t"9_%TLu$ϛ&Z iR׸\=<{Bb)5kUQ>ө!..:&FHC~ K d8߁>nhߧyQ[:v;ٶmaKYV Chq_YYY͛7/[L3M 劇李XjkDzC !'(~PB:ւNmX@.oBrWaiiIENDB`oneconf-0.3.9/test/data/syncdatatests/host_sync_remove_other_host/package_list_AAAA0000664000000000000000000000015012624560235025564 0ustar {"libFool": {"auto": true}, "kiki": {"auto": false}, "unity": {"auto": false}, "libFoo": {"auto": true}}oneconf-0.3.9/test/data/syncdatatests/silo_unshare_other_host0000664000000000000000000000107312624560235021505 0ustar (dp1 S'fake_network_delay' p2 I2 sS'server_response_error' p3 I00 sS'update_machine_error' p4 I00 sS'hosts_metadata' p5 (dp6 S'AAAA' p7 (dp8 S'hostname' p9 S'aaaa' p10 sS'logo_checksum' p11 NsS'packages_checksum' p12 S'packageaaaa' p13 sssS'delete_machine_error' p14 I00 sS'update_packages_error' p15 I00 sS'list_packages_error' p16 I00 sS'list_machines_error' p17 I00 sS'get_machine_logo_error' p18 I00 sS'update_machine_logo_error' p19 I00 sS'packages_metadata' p20 (dp21 g7 (dp22 S'baz' p23 (dp24 Vauto p25 I00 ssS'foo' p26 (dp27 g25 I00 ssVbar p28 (dp29 g25 I01 ssss.oneconf-0.3.9/test/data/wallpaper.png0000664000000000000000000000420512624560235014435 0ustar PNG  IHDRdL\sRGB pHYs  tIME 3WItEXtCommentCreated with GIMPWIDATxO cM4 ]K6eUN]e٤i.]oݮv7)LՖI[ZR.YYm|}m|iP:-i~?#{~64.  X@,b  @,Xb @, Xb  X@,b   x @CBG'!KbA9HlxݽdT|b#T;'BA|/dx#3 Kpr&6ϖbd,P姟XzA]I<^JƓ38x2&6ֿ[\)% $=OO1Yu :WޡOgƾ>"2%KZW3o\7f:*XS]υw+ea2 !-}/p&5I ikj @O19kKsx% ?3O f0x{˺V4;,]co=([VeDg865tYg?Mx]e:dJI6z`Uiܰ~릲#QEq*l?JY BlziǖfӷISrJ<}ohQeZ, GϾeBVmC /_LG):ڪ@[3Thj'r\iHc[i7C6)ӋhϽ[V<ut`=D׼, Si*ɱ頧T%|sT}=җ*}x¼D|gY"l#&RbA^+v)S5j|F,iGF V P7p_OҪ&1i{.f?hl c VI<1Y};9l8 ۂ\94" #fvXKtx Isʐt~Vx-72nXl{)k<}9U(+^gJ]Q7:Jo&~#m~c۞he7 >6u<[l7L$'5VqǺjX [拲M8{TKmae5DžIENDB`oneconf-0.3.9/test/run0000775000000000000000000000164412624560235011565 0ustar #!/usr/bin/env python3 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- ### BEGIN LICENSE # Copyright (C) 2011 Didier Roche # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . ### END LICENSE # ensure we start at the root trunk dir import os os.chdir(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..')) import nose nose.run() oneconf-0.3.9/po/0000775000000000000000000000000012624561776010500 5ustar oneconf-0.3.9/po/oneconf.pot0000664000000000000000000001056412624560235012646 0ustar # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2012-11-21 18:27-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: .././oneconf-service:70 #, c-format msgid "Usage: %prog [options]" msgstr "" #: .././oneconf-service:73 msgid "Enable debug mode." msgstr "" #: .././oneconf-service:75 msgid "Use the mock infrastructure." msgstr "" #: .././oneconf-service:102 msgid "" "An OneConf service is already running, shut it down with oneconf-query --stop" msgstr "" #: .././oneconf-query:46 msgid "Installed package:" msgstr "" #: .././oneconf-query:53 msgid "Additional packages: (package to install)" msgstr "" #: .././oneconf-query:56 msgid "Missing packages: (package to remove)" msgstr "" #: .././oneconf-query:62 msgid "Listing this host stored in OneConf:" msgstr "" #: .././oneconf-query:64 msgid "Hosts stored for OneConf:" msgstr "" #: .././oneconf-query:76 msgid "You can't define --all-packages, --manual-packages or --hosts together." msgstr "" #: .././oneconf-query:80 msgid "" "You can't define --list, --diff, --update, --async-update, --share-" "inventory, --stop, --get-last-sync together." msgstr "" #: .././oneconf-query:84 #, c-format msgid "%s isn't compatible with %s" msgstr "" #: .././oneconf-query:90 #, c-format msgid "usage: %prog [options]" msgstr "" #: .././oneconf-query:94 msgid "" "Current diff between this machine and another provided by hostname/hostid" msgstr "" #: .././oneconf-query:98 msgid "List stored package (default for local hostid) or host lists" msgstr "" #: .././oneconf-query:101 msgid "Get last sync date" msgstr "" #: .././oneconf-query:103 msgid "Update the package list in store" msgstr "" #: .././oneconf-query:106 msgid "Perform async update of the package list in store" msgstr "" #: .././oneconf-query:108 msgid "Stop oneconf service" msgstr "" #: .././oneconf-query:110 msgid "Enable debug mode (use --direct)" msgstr "" #: .././oneconf-query:112 msgid "Don't use dbus for the request" msgstr "" #: .././oneconf-query:117 msgid "Get all installed packages from storage" msgstr "" #: .././oneconf-query:120 msgid "Get only manual installed packages from storage" msgstr "" #: .././oneconf-query:123 msgid "All available hosts from storage (only with list)" msgstr "" #: .././oneconf-query:126 msgid "This host (only with list)" msgstr "" #: .././oneconf-query:132 msgid "Specify target hostname" msgstr "" #: .././oneconf-query:134 msgid "Specify target hostid" msgstr "" #: .././oneconf-query:140 msgid "Share this inventory on the web" msgstr "" #: .././oneconf-query:144 msgid "Hide this inventory on the web" msgstr "" #: .././oneconf-query:226 msgid "hostid and hostname can't be provided together." msgstr "" #: .././oneconf-query:231 .././oneconf-query:241 msgid "You can't use hostid or hostname when updating." msgstr "" #: .././oneconf-query:234 .././oneconf-query:244 msgid "You can't define --package, --host or --hosts when updating." msgstr "" #: .././oneconf-query:265 msgid "You have to provide either hostid or hostname for getting a diff." msgstr "" #: .././oneconf-query:283 .././oneconf-query:292 msgid "" "You can't define --package, --host or --hosts when changing show inventory " "status." msgstr "" #: .././oneconf-query:289 msgid "You can't use hostid or hostname when changing show inventory status." msgstr "" #: .././oneconf/dbusconnect.py:210 #, python-format msgid "Wasn't able to request stopping the service: %s" msgstr "" #: .././oneconf/directconnect.py:87 msgid "" "Nothing done: in direct mode, there is no communication with the service" msgstr "" #: .././oneconf/hosts.py:218 msgid "No hostname registered for this id" msgstr "" #: .././oneconf/hosts.py:240 msgid "" "Multiple hostid registered for this hostname. Use --list --host to get the " "hostid and use the --hostid option." msgstr "" #: .././oneconf/hosts.py:244 msgid "No hostid registered for this hostname" msgstr "" #: .././oneconf/hosts.py:316 .././oneconf/hosts.py:319 msgid "Was never synced" msgstr "" oneconf-0.3.9/po/POTFILES.in0000664000000000000000000000105012624560235012236 0ustar ./oneconf-service ./oneconf-query ./oneconf/__init__.py ./oneconf/dbusconnect.py ./oneconf/directconnect.py ./oneconf/distributor/Test.py ./oneconf/distributor/Ubuntu.py ./oneconf/distributor/__init__.py ./oneconf/enums.py ./oneconf/hosts.py ./oneconf/networksync/__init__.py ./oneconf/networksync/fake_webcatalog_silo.py ./oneconf/networksync/infraclient_fake.py ./oneconf/networksync/infraclient_pristine.py ./oneconf/networksync/netstatus.py ./oneconf/networksync/ssohandler.py ./oneconf/packagesethandler.py ./oneconf/paths.py ./oneconf/version.py oneconf-0.3.9/setup.py0000664000000000000000000000416612624560235011570 0ustar #!/usr/bin/env python from setuptools import setup from DistUtilsExtra.command import * import re import sys import glob from codecs import open from subprocess import Popen, PIPE # update version.py with open('debian/changelog', encoding='utf-8') as fp: line = fp.readline() m = re.match("^[\w-]+ \(([\w\.~]+)\) ([\w-]+);", line) VERSION = m.group(1) CODENAME = m.group(2) DISTRO = Popen(["lsb_release", "-s", "-i"], stdout=PIPE, universal_newlines=True).communicate()[0].strip() RELEASE = Popen(["lsb_release", "-s", "-r"], stdout=PIPE, universal_newlines=True).communicate()[0].strip() #should be replaced by $USR oneconf_service_path = "/usr/share/oneconf/oneconf-service" # Only write the files if we're building. if any(argv for argv in sys.argv if 'build' in argv): with open('oneconf/version.py', 'w', encoding='utf-8') as fp: fp.write("""\ VERSION='%s' CODENAME='%s' DISTRO='%s' RELEASE='%s' """ % (VERSION, CODENAME, DISTRO, RELEASE)) with open('misc/com.ubuntu.OneConf.service', 'w', encoding='utf-8') as fp: fp.write("""\ [D-BUS Service] Name=com.ubuntu.OneConf Exec=%s """ % oneconf_service_path) # The scripts only work with Python 3. if sys.version_info[0] == 3: scripts = ['oneconf-query', 'oneconf-service', 'misc/oneconf-update'] else: scripts = [] # real setup setup(name="oneconf", version=VERSION, scripts=scripts, packages = ['oneconf', 'oneconf.distributor', 'oneconf.networksync', ], data_files=[ ('share/oneconf/data/images/', glob.glob("data/images/*.png")), ('share/dbus-1/services/', ["misc/com.ubuntu.OneConf.service"]), ], cmdclass = { "build" : build_extra.build_extra, "build_i18n" : build_i18n.build_i18n, "build_help" : build_help.build_help, "build_icons" : build_icons.build_icons}, test_suite = 'nose.collector', test_requires = [ 'piston_mini_client', ], ) oneconf-0.3.9/COPYING0000664000000000000000000010437412624560235011113 0ustar GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . oneconf-0.3.9/debian/0000775000000000000000000000000012624561777011305 5ustar oneconf-0.3.9/debian/oneconf.install0000664000000000000000000000001012624560235014277 0ustar usr/bin oneconf-0.3.9/debian/oneconf-common.install0000664000000000000000000000001212624560235015567 0ustar usr/share oneconf-0.3.9/debian/oneconf.links0000664000000000000000000000022612624560235013762 0ustar usr/share/oneconf/oneconf-query usr/bin/oneconf-query usr/share/oneconf/oneconf-update usr/share/update-notifier/plugins/cache-changed/oneconf-update oneconf-0.3.9/debian/python-oneconf.install0000664000000000000000000000004012624560235015621 0ustar usr/lib/python2.*/dist-packages oneconf-0.3.9/debian/compat0000664000000000000000000000000212624560235012467 0ustar 9 oneconf-0.3.9/debian/dirs0000664000000000000000000000006012624560235012151 0ustar usr/share/update-notifier/plugins/cache-changed oneconf-0.3.9/debian/changelog0000664000000000000000000002771412624561766013170 0ustar oneconf (0.3.9) xenial; urgency=medium * d/control: - Drop the explicit Python 2 dependencies for oneconf-common since the package contains no Python code. (LP: #1440375) - Add ${python3:Depends} to oneconf binary package. - Bump Standards-Version with no other changes necessary. -- Barry Warsaw Tue, 17 Nov 2015 17:51:28 -0500 oneconf (0.3.8) wily; urgency=medium * Decode the data received from the server if necessary. Use ast.literal_eval() to parse it. * Hostnames can contain '.' characters - fix the regex. -- Iain Lane Tue, 05 May 2015 17:55:22 +0100 oneconf (0.3.7) trusty; urgency=medium * d/rules: Override dh_python3 command to force oneconf-query to use the generic /usr/bin/python3 shebang. (LP: #1296325) -- Barry Warsaw Mon, 24 Mar 2014 11:23:02 -0400 oneconf (0.3.6) trusty; urgency=medium * d/control: Build-Depend on python3-all. * paths.py: Ensure a predictable FAKE_WALLPAPER path in all supported Python versions, since __file__ is absolute in Python 3.4 and relative in all older Pythons. (LP: #1269898) -- Barry Warsaw Thu, 16 Jan 2014 13:25:41 -0500 oneconf (0.3.5) saucy; urgency=low * oneconf/distributor/Ubuntu.py: close the apt cache when we are done using it. Thanks to Jason Conti for the patch. (LP: #1051935) -- Brian Murray Tue, 27 Aug 2013 16:26:54 -0700 oneconf (0.3.4) saucy; urgency=low * Drop XS-Testsuite: header. The autopkgtest has never succeeded and needs some nontrivial work, and this is blocking packages like pygobject from propagating. -- Martin Pitt Tue, 30 Jul 2013 10:45:53 +0200 oneconf (0.3.3) raring; urgency=low * d/tests/unittests: Remove -x debugging artifact and run the nose tests under -q instead of -vv to inhibit stderr output. (LP: #1102875) * oneconf-query: Use gettext.install() to install the _() function. (LP: #1103192) -- Barry Warsaw Wed, 23 Jan 2013 18:30:05 -0500 oneconf (0.3.2) raring; urgency=low * setup.py: Only install the command line scripts for Python 3. (LP: #1102715) -- Barry Warsaw Tue, 22 Jan 2013 11:24:38 -0500 oneconf (0.3.1) raring; urgency=low * Add a missing Replaces due to files moving between packages (LP: #1102713) -- Didier Roche Tue, 22 Jan 2013 13:18:59 +0100 oneconf (0.3) raring; urgency=low * Port to Python 3 and split the packaging: - debian/compat: Bump to 9. - debian/control: + debhelper >= 9 + Add python3-* build dependencies. + Add new binary packages: oneconf-common, python-oneconf, python3-oneconf, oneconf (the latter has the executables). + Remove python-imaging since PIL is not available for Python 3. + Switch to python3-oauthlib. + Standards-Version: 3.9.4 + Add XS-Testsuite for DEP 8. - debian/*.install: Added for new binary package layout. - debian/*.links: Added. - debian/rules: + Build for both Python 2 and Python 3. + Run the test suite where possible. - debian/tests/*: Enable DEP 8 autopkgtests. - oneconf/version.py: + Bump to 0.3 + Codename: raring -- Barry Warsaw Tue, 15 Jan 2013 10:52:09 -0500 oneconf (0.2.9.1) quantal; urgency=low * handle a regression when the wallpaper isn't reachable + test case (LP: #1010239) -- Didier Roche Fri, 08 Jun 2012 15:30:03 +0200 oneconf (0.2.9) quantal; urgency=low * New release: - fix some typos and enhanced translations - fix some tests to be able to pass on others machines - prevent crashes on mint and elementary which doesn't ship a file for their distro. Added test cases for it (LP: #1006701) * debian/control: - python-gtk2 removed, not needed anymore -- Didier Roche Mon, 04 Jun 2012 13:43:51 +0200 oneconf (0.2.8) precise; urgency=low [ Michael Vogt ] * Fix software-center crashed with KeyError in get_hostid_pending_change() (LP: #941193) [ Didier Roche ] * Move saving facility to an utils module to reuse safe saving accross the whole application. Removed a deprecated call as well (LP: #944738, #842198) * Recover gracefully if any of the json file is broken (LP: #849037) * Fix turning off oneconf is USC doesn't work (LP: #964659) * oneconf-service crashed with KeyError in check_if_refresh_needed(): 'packages_checksum' (LP: #940710) * Added/Enhanced tests for all the above * debian/control: - Bump Standards-Version: to latest -- Didier Roche Tue, 10 Apr 2012 15:11:17 +0200 oneconf (0.2.6.9) precise; urgency=low * setup.py: Include missing oneconf.distributor package. (LP: #934624) -- Gediminas Paulauskas Sun, 19 Feb 2012 19:03:16 +0200 oneconf (0.2.6.8ubuntu1) precise; urgency=low * po/POTFILES.in Update with current list of source files. (LP: #934592) -- Barry Warsaw Fri, 17 Feb 2012 18:56:11 -0500 oneconf (0.2.6.8) precise; urgency=low * New release: - ensure we keep staged pending action of the server told us the operation failed (LP: #932715) - preventing running as root (LP: #834458) - fix a typo when gsettings return null (LP: #871783) - oneconf-service crashed with UnboundLocalError in process_sync(): local variable 'packages_checksum' referenced before assignment (LP: #908759) - import the dbus exception at the right place (LP: #889867) - add a lot of tests * debian/control: - change python-gobject dep by python-gi -- Didier Roche Wed, 15 Feb 2012 17:22:39 +0100 oneconf (0.2.6.7) oneiric; urgency=low * restore MIN_TIME_WITHOUT_ACTIVITY to 5 min -- Didier Roche Fri, 23 Sep 2011 18:27:24 +0200 oneconf (0.2.6.6) oneiric; urgency=low * Don't crash if translation domain for that language not available (LP: #856576) -- Didier Roche Fri, 23 Sep 2011 08:26:29 +0200 oneconf (0.2.6.5) oneiric; urgency=low * New release: - fix a crash when using --help having utf8 translations - fix if you just enable sso login for the first time in software-center, oneconf won't sync before restarting (LP: #855373) - emitting a last sync accurate date as soon as the sync is done (LP: #855345) -- Didier Roche Thu, 22 Sep 2011 14:46:58 +0200 oneconf (0.2.6.4) oneiric; urgency=low * New release: - Convert data receive rom OneConf server from strings to json objects - Empty content can be valid, like removing latest other host - Protect against a BadStatusLine and RedirectLimit http requests return (LP: #852296, #851567) - Don't continue syncing if we even can't get a successfull list machines data (LP: #854685) -- Didier Roche Wed, 21 Sep 2011 09:13:01 +0200 oneconf (0.2.6.3) oneiric; urgency=low * Fix no logging result not handled error (LP: #851132) * Protect against apps.ubuntu.com not being available (LP: #851169) -- Didier Roche Fri, 16 Sep 2011 08:07:23 +0200 oneconf (0.2.6.2) oneiric; urgency=low * Prepare synchronization for once the servers will be opened -- Didier Roche Thu, 15 Sep 2011 18:31:04 +0200 oneconf (0.2.6.1) oneiric; urgency=low * debian/control: - add missing python-imaging dependency (LP: #839288) - recommends gtk3 version of software-center -- Didier Roche Mon, 05 Sep 2011 09:26:11 +0200 oneconf (0.2.6) oneiric; urgency=low * New release: - Remove the uscplugins bits, now in software center for the gtk3 version (LP: #838623) - Fix oneconf-service crashed with OSError in _execute_child(): [Errno 2] No such file or directory (LP: #829570) - Fix oneconf-query/service crashed with DBusException in __new__() (LP: #824771, #832029) - Fix misc oneconf-query/service crashed with DBusException in call_blocking(): (LP: #824789, #826687, #831651, #834394) - Translation improvements (LP: #828897) - Protect against oneconf-service crashed with ValueError in raw_decode(): No JSON object could be decoded (LP: #829260) * debian/rules: - install now oneconf in the python public library directory. Needed as now access directly from software center. -- Didier Roche Thu, 01 Sep 2011 17:36:22 +0200 oneconf (0.2.5) oneiric; urgency=low * oneconf/hosts.py: Don't use gi.repository.Gio, as we are using static bindings here still (gtk, gobject, etc.); call "gsettings" instead. This can be put back once the GUI etc. gets ported to GI, and we switch to the ported software-center. (LP: #829186) -- Martin Pitt Fri, 19 Aug 2011 07:21:32 +0200 oneconf (0.2.4) oneiric; urgency=low * Don't crash on invalid images (LP: #824844) -- Didier Roche Fri, 12 Aug 2011 08:16:05 +0200 oneconf (0.2.3) oneiric; urgency=low * New release: - ensure only one OneConf service can run at a time - enable asking for the service to stop -- Didier Roche Thu, 11 Aug 2011 17:49:12 +0200 oneconf (0.2.2) oneiric; urgency=low * New release: - adapt the usc plugin to latest share_inventory API -- Didier Roche Wed, 10 Aug 2011 16:29:42 +0200 oneconf (0.2.1) oneiric; urgency=low * New release: - get the toolbar hiding when in detail view. Thanks Gary Lasker - compute an update the first time if current host list necessary and no update done - add translation support and generate template - provide the ONECONF_HOST environment variable to define a fake hostid and hostname for testing purpose - enable remotely to set some pending state for other hosts, like hide inventory. The effect isn't immediate and is queued in a pending file until next connexion to the server. Add some safeguards for corner cases -- Didier Roche Fri, 05 Aug 2011 17:46:41 +0200 oneconf (0.2) oneiric; urgency=low * New release: - move from desktopcouch to a json cache file base version, build a server/client protocol based on ubuntu-webcatalog (server part will be oneline soon). (LP: #720498, #715514, #748517) - adapt to latest software-center in oneiric - harden a lot regards to bug handling timeouts and such (LP: #616463, #625554, #804768, #594394) - optimize startup time with json and don't wait on server side sync (LP: #638142, #780651) - adapt now with ubuntu sso and triggers the login dialog on fresh install (LP: #674537, #708187) * debian/control, debian/rules: - we really don't depend on desktopcouch now - deps on ubuntu-sso-client now - transition to dh_python2 - remove other deprecated deps - bump Standards-Version -- Didier Roche Mon, 01 Aug 2011 12:30:21 +0200 oneconf (0.1.3) natty; urgency=low * Update to Natty USC API (fix crashes at start) * Hide the non working UI with new ubuntu one. -- Didier Roche Fri, 08 Apr 2011 19:03:45 +0200 oneconf (0.1.2) maverick; urgency=low * New release: - fix possible rash on login to u1account (LP: #650911) - be compatible with previous OneConf format (LP: #650637) - fix some hanging when u1dialog window triggered at wrong time - protect pane refresh in multiple threads - be compatible with incoming USC, enhancing the loop performance when non apps not visible (LP: #653787) (still compatible with previous one) -- Didier Roche Mon, 04 Oct 2010 14:09:28 +0200 oneconf (0.1.1) maverick; urgency=low * data/ui/u1inventorydialog.ui: - fix a u1 typo -- Didier Roche Thu, 09 Sep 2010 12:29:12 +0200 oneconf (0.1) maverick; urgency=low * Initial release. -- Didier Roche Tue, 27 Jul 2010 17:24:14 +0200 oneconf-0.3.9/debian/copyright0000664000000000000000000000053412624560235013226 0ustar Format-Specification: http://wiki.debian.org/Proposals/CopyrightFormat Upstream-Name: oneconf Upstream-Maintainer: Didier Roche Upstream-Source: http://launchpad.net/oneconf Files: * Copyright: (C) 2010 Canonical License: GPL-3 The full text of the GPL is distributed in /usr/share/common-licenses/GPL-3 on Debian systems. oneconf-0.3.9/debian/rules0000775000000000000000000000272512624560235012357 0ustar #!/usr/bin/make -f #DH_VERBOSE=1 share:=usr/share/oneconf # Prevent setuptools/distribute from accessing the internet. export http_proxy = http://127.0.9.1:9 PYTHON2=$(shell pyversions -vr) PYTHON3=$(shell py3versions -vr) %: dh $@ --with python2,python3 ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS))) test-python%: # The tests don't work well when run as one unit, so run them # individually. python$* setup.py nosetests -vv --test test.test_mainfeatures python$* setup.py nosetests -vv --test test.test_syncing # I don't know of a good way to run this test in the schroot, even # using xvfb to give us an X display. Rely on DEP 8 tests to cover # these tests. #xvfb-run python$* setup.py nosetests -vv --test test.test_daemon override_dh_auto_test: $(PYTHON2:%=test-python%) $(PYTHON3:%=test-python%) endif build-python%: python$* setup.py build override_dh_auto_build: $(PYTHON3:%=build-python%) dh_auto_build override_dh_python3: dh_python3 --shebang=/usr/bin/python3 install-python%: python$* setup.py install --root=$(CURDIR)/debian/tmp --install-layout=deb override_dh_auto_install: $(PYTHON3:%=install-python%) dh_auto_install # Move the scripts to $(SHARE) and relink some again later. The # scripts will be included in the "oneconf" binary package mkdir -p debian/oneconf/$(share) mv debian/tmp/usr/*bin/* debian/oneconf/$(share) override_dh_auto_clean: dh_auto_clean rm -rf build rm -rf *.egg-info override_dh_install: dh_install --fail-missing oneconf-0.3.9/debian/control0000664000000000000000000000640512624560245012702 0ustar Source: oneconf Section: python Priority: extra Build-Depends: debhelper (>= 9), lsb-release, gettext, intltool, xvfb, python, python-setuptools, python-distutils-extra, python-nose, python-mock, python-xdg, python-gi, python-piston-mini-client, python-dbus, python3-all, python3-setuptools, python3-distutils-extra, python3-nose, python3-mock, python3-xdg, python3-gi, python3-piston-mini-client, python3-dbus Maintainer: Didier Roche Standards-Version: 3.9.6 X-Python-Version: >= 2.7 X-Python3-Version: >= 3.3 Package: oneconf-common Architecture: all Depends: ${misc:Depends}, Replaces: oneconf (<< 0.3) Description: synchronize your configuration data over the network OneConf provides the ability to sync your computer's configuration data over the network. . It integrates nicely with the Ubuntu Software Center to compare sets of installed software between computers. A command line tool also provides for the same functionality. . This is the packaging containing common files. Package: python-oneconf Architecture: all Depends: ${misc:Depends}, ${python:Depends}, oneconf-common, python-dbus, python-oauthlib, python-gi, python-apt, python-piston-mini-client, python-httplib2, python-xdg Replaces: oneconf (<< 0.3) Description: synchronize your configuration data over the network (Python 2) OneConf provides the ability to sync your computer's configuration data over the network. . It integrates nicely with the Ubuntu Software Center to compare sets of installed software between computers. A command line tool also provides for the same functionality. . This is the Python 2 support library. Package: python3-oneconf Architecture: all Depends: ${misc:Depends}, ${python3:Depends}, oneconf-common, python3-dbus, python3-oauthlib, python3-gi, python3-apt, python3-piston-mini-client, python3-httplib2, python3-xdg Description: synchronize your configuration data over the network (Python 3) OneConf provides the ability to sync your computer's configuration data over the network. . It integrates nicely with the Ubuntu Software Center to compare sets of installed software between computers. A command line tool also provides for the same functionality. . This is the Python 3 support library. Package: oneconf Architecture: all Depends: ${misc:Depends}, ${python3:Depends}, oneconf-common, python3-oneconf, ubuntu-sso-client Recommends: software-center (>= 4.1.21), update-notifier (>= 0.103), Description: synchronize your configuration data over the network OneConf provides the ability to sync your computer's configuration data over the network. . It integrates nicely with the Ubuntu Software Center to compare sets of installed software between computers. A command line tool also provides for the same functionality. oneconf-0.3.9/debian/python3-oneconf.install0000664000000000000000000000002012624560235015702 0ustar usr/lib/python3 oneconf-0.3.9/debian/tests/0000775000000000000000000000000012624561776012446 5ustar oneconf-0.3.9/debian/tests/unittests0000664000000000000000000000064112624560235014421 0ustar #!/bin/sh # autopkgtest check: Run the upstream unittests. # (C) 2013 Canonical Ltd. # Author: Barry Warsaw set -e # Run the tests individually, since running them via the tests/run script does # not always complete. python3 setup.py nosetests -q --test test.test_mainfeatures python3 setup.py nosetests -q --test test.test_syncing xvfb-run python3 setup.py nosetests -q --test test.test_daemon oneconf-0.3.9/debian/tests/control0000664000000000000000000000022112624560235014031 0ustar Tests: unittests Depends: @, xvfb, python3-nose, python3-mock Restrictions: rw-build-tree build-needed # LP: #1102715 Tests: version Depends: @ oneconf-0.3.9/debian/tests/version0000664000000000000000000000033712624560235014046 0ustar #!/bin/sh # autopkgtest check: Make sure --version succeeds. LP: #1102715 # (C) 2013 Canonical Ltd. # Author: Barry Warsaw set -e # It's enough that this doesn't fail. /usr/bin/oneconf-query --version oneconf-0.3.9/oneconf/0000775000000000000000000000000012624561776011511 5ustar oneconf-0.3.9/oneconf/utils.py0000664000000000000000000000247112624560235013214 0ustar # Copyright (C) 2012 Canonical # # Authors: # Didier Roche # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 3. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import json import logging import os LOG = logging.getLogger(__name__) def save_json_file_update(file_uri, content): '''Save local file in an atomic transaction''' if not content: LOG.warning("Empty content saved as \"\" for %s" % file_uri) content = {} LOG.debug("Saving updated %s to disk", file_uri) new_file = file_uri + '.new' try: with open(new_file, 'w') as f: json.dump(content, f) os.rename(new_file, file_uri) return True except IOError: LOG.error("Can't save update file for %s", file_uri) return False oneconf-0.3.9/oneconf/paths.py0000664000000000000000000000742512624560235013177 0ustar # -*- coding: utf-8 -*- # Copyright (C) 2011 Canonical # # Authors: # Didier Roche # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 3. # # This program is distributed in the hope that it will be useful, but WITHOUTa # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import os try: from configparser import NoSectionError, NoOptionError, RawConfigParser except ImportError: # Python 2 from ConfigParser import NoSectionError, NoOptionError, RawConfigParser from xdg import BaseDirectory as xdg ONECONF_OVERRIDE_FILE = "/tmp/oneconf.override" ONECONF_DATADIR = '/usr/share/oneconf/data' ONECONF_CACHE_DIR = os.path.join(xdg.xdg_cache_home, "oneconf") PACKAGE_LIST_PREFIX = "package_list" OTHER_HOST_FILENAME = "other_hosts" PENDING_UPLOAD_FILENAME = "pending_upload" HOST_DATA_FILENAME = "host" LOGO_PREFIX = "logo" LAST_SYNC_DATE_FILENAME = "last_sync" _datadir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data") # In both Python 2 and 3, _datadir will be a relative path, however, in Python # 3 it will start with "./" while in Python 2 it will start with just the file # name. Normalize this, since the path string is used in the logo_checksum # calculation. if not os.path.isabs(_datadir) and not _datadir.startswith('./'): _datadir = os.path.join(os.curdir, _datadir) if not os.path.exists(_datadir): # take the paths file if loaded from networksync module # # 2014-03-17 barry: It's probably not a good idea to use __file__, since # the behavior of that has changed between Python 3.3 and 3.4. Prior to # 3.4, __file__ was a relative path, but in 3.4 it became absolute (which # it always should have been). Because the file's *path* is the input to # the logo checksum (as opposed to the file's contents, because...?) this # value actually matters. # # However, making the FAKE_WALLPAPER path below absolute breaks the # package's build because inside a chroot, the absolute path of __file__ # is unpredictable. LP: #1269898. # # The solution then is to make the FAKE_WALLPAPER path relative to the # current working directory, via os.path.relpath(). So first, we ensure # it's absolute (for older Pythons) and then relpath it. *That's* the # path that will be the input to the SHA224 checksum. parent = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) _datadir = os.path.join(parent, "data") if not os.path.exists(_datadir): _datadir = ONECONF_DATADIR LOGO_BASE_FILENAME = os.path.join(_datadir, 'images', 'computer.png') WEBCATALOG_SILO_DIR = "/tmp" FAKE_WALLPAPER = None # Fake wallpaper for tests FAKE_WALLPAPER_MTIME = None # Fake wallpaper for tests config = RawConfigParser() try: config.read(ONECONF_OVERRIDE_FILE) ONECONF_CACHE_DIR = config.get('TestSuite', 'ONECONF_CACHE_DIR') WEBCATALOG_SILO_DIR = config.get('TestSuite', 'WEBCATALOG_SILO_DIR') FAKE_WALLPAPER = os.path.relpath(os.path.abspath(os.path.join( os.path.dirname(_datadir), config.get('TestSuite', 'FAKE_WALLPAPER')))) try: FAKE_WALLPAPER_MTIME = config.get('TestSuite', 'FAKE_WALLPAPER_MTIME') except NoOptionError: FAKE_WALLPAPER_MTIME = None except NoSectionError: pass WEBCATALOG_SILO_SOURCE = os.path.join(WEBCATALOG_SILO_DIR, "source") WEBCATALOG_SILO_RESULT = os.path.join(WEBCATALOG_SILO_DIR, "result") oneconf-0.3.9/oneconf/packagesethandler.py0000664000000000000000000001521012624560235015514 0ustar # Copyright (C) 2010 Canonical # # Authors: # Didier Roche # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 3. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import hashlib import json import logging import os from pprint import pformat LOG = logging.getLogger(__name__) from oneconf.hosts import Hosts from oneconf.distributor import get_distro from oneconf.paths import PACKAGE_LIST_PREFIX from oneconf import utils class PackageSetInitError(Exception): """An error occurred, preventing the package set to initialize.""" class PackageSetHandler(object): """ Direct access to database for getting and updating the list """ def __init__(self, hosts=None): self.hosts = hosts if not hosts: self.hosts = Hosts() self.distro = get_distro() if not self.distro: raise PackageSetInitError( "Can't initialize PackageSetHandler: no valid distro provided") self.last_storage_sync = None # create cache for storage package list, indexed by hostid self.package_list = {} def update(self): '''update the store with package list''' hostid = self.hosts.current_host['hostid'] LOG.debug("Updating package list") newpkg_list = self.distro.compute_local_packagelist() LOG.debug("Creating the checksum") # We need to get a reliable checksum for the dictionary in # newpkg_list. Dictionary order is unpredictable, so to get a # reproducible checksum, we need a predictable string representation # of the dictionary. pprint.pformat() seems to give us the best # option here since it guarantees that dictionary keys are sorted. # hashlib works on bytes only though, so assume utf-8. hash_input = pformat(newpkg_list).encode('utf-8') checksum = hashlib.sha224(hash_input).hexdigest() LOG.debug("Package list need refresh") self.package_list[hostid] = {'valid': True, 'package_list': newpkg_list} utils.save_json_file_update(os.path.join(self.hosts.get_currenthost_dir(), '%s_%s' % (PACKAGE_LIST_PREFIX, hostid)), self.package_list[hostid]['package_list']) if self.hosts.current_host['packages_checksum'] != checksum: self.hosts.current_host['packages_checksum'] = checksum self.hosts.save_current_host() LOG.debug("Update done") def get_packages(self, hostid=None, hostname=None, only_manual=False): '''get all installed packages from the storage''' hostid = self.hosts.get_hostid_from_context(hostid, hostname) LOG.debug ("Request for package list for %s with only manual packages reduced scope to: %s", hostid, only_manual) package_list = self._get_installed_packages(hostid) if only_manual: package_list = [ package_elem for package_elem in package_list if not package_list[package_elem]["auto"]] return package_list def _get_installed_packages(self, hostid): '''get installed packages from the storage or cache Return: uptodate package_list''' need_reload = False try: if self.package_list[hostid]['valid']: LOG.debug("Hit cache for package list") package_list = self.package_list[hostid]['package_list'] else: need_reload = True except KeyError: need_reload = True if need_reload: self.package_list[hostid] = { 'valid': True, 'package_list': self._get_packagelist_from_store(hostid), } return self.package_list[hostid]['package_list'] def diff(self, distant_hostid=None, distant_hostname=None): """get a diff from current package state from another host This function can be use to make a diff between all packages installed on both computer, use_cache Return: (packages_to_install (packages in distant_hostid not in local_hostid), packages_to_remove (packages in local hostid not in distant_hostid)) """ distant_hostid = self.hosts.get_hostid_from_context( distant_hostid, distant_hostname) LOG.debug("Collecting all installed packages on this system") local_package_list = set( self.get_packages(self.hosts.current_host['hostid'], False)) LOG.debug("Collecting all installed packages on the other system") distant_package_list = set(self.get_packages(distant_hostid, False)) LOG.debug("Comparing") packages_to_install = [ x for x in sorted(distant_package_list) if x not in local_package_list] packages_to_remove = [ x for x in sorted(local_package_list) if x not in distant_package_list] # for Dbus which doesn't like empty list if not packages_to_install: packages_to_install = '' if not packages_to_remove: packages_to_remove = '' return packages_to_install, packages_to_remove def _get_packagelist_from_store(self, hostid): '''load package list for every computer in cache''' LOG.debug('get package list from store for hostid: %s' % hostid) # load current content in cache try: with open(os.path.join(self.hosts.get_currenthost_dir(), '%s_%s' % (PACKAGE_LIST_PREFIX, hostid)), 'r') as f: # can be none in corrupted null file pkg_list = json.load(f) except (IOError, ValueError): LOG.warning ("no valid package list stored for hostid: %s" % hostid) pkg_list = None if pkg_list is None: pkg_list = {} # there is no way that no package is installed in current host # At least, there is oneconf ;) Ask for refresh if hostid == self.hosts.current_host['hostid']: LOG.debug ("Processing first update for current host") self.update() pkg_list = self.package_list[hostid]['package_list'] return pkg_list oneconf-0.3.9/oneconf/enums.py0000664000000000000000000000235212624560235013201 0ustar #!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (C) 2011 Canonical # # Authors: # Didier Roche # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 3. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA try: from configparser import NoSectionError, RawConfigParser except ImportError: # Python 2 from ConfigParser import NoSectionError, RawConfigParser from oneconf.paths import ONECONF_OVERRIDE_FILE config = RawConfigParser() try: config.read(ONECONF_OVERRIDE_FILE) MIN_TIME_WITHOUT_ACTIVITY = config.getint( 'TestSuite', 'MIN_TIME_WITHOUT_ACTIVITY') except NoSectionError: MIN_TIME_WITHOUT_ACTIVITY = 60 * 5 ONECONF_SERVICE_NAME = "com.ubuntu.OneConf" oneconf-0.3.9/oneconf/dbusconnect.py0000664000000000000000000001707212624560235014366 0ustar # Copyright (C) 2010 Canonical # # Authors: # Didier Roche # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 3. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import dbus import dbus.service from gi.repository import GLib import logging import sys from gettext import gettext as _ LOG = logging.getLogger(__name__) from oneconf.enums import ONECONF_SERVICE_NAME HOSTS_OBJECT_NAME = "/com/ubuntu/oneconf/HostsHandler" PACKAGE_SET_INTERFACE = "com.ubuntu.OneConf.HostsHandler.PackageSetHandler" HOSTS_INTERFACE = "com.ubuntu.OneConf.HostsHandler.Hosts" ONECONF_DBUS_TIMEOUT = 300 def none_to_null(var): '''return var in dbus compatible format''' if not var: var = '' return var class DbusHostsService(dbus.service.Object): """ Dbus service, daemon side """ def __init__(self, loop): '''registration over dbus''' bus_name = dbus.service.BusName(ONECONF_SERVICE_NAME, bus=dbus.SessionBus()) dbus.service.Object.__init__(self, bus_name, HOSTS_OBJECT_NAME) # Only import oneconf module now for only getting it on server side from oneconf.hosts import Hosts self.hosts = Hosts() self._packageSetHandler = None self.activity = False self.synchandler = None self.loop = loop # TODO: can be a decorator, handling null case and change the API so that if it returns # the None value -> no result def get_packageSetHandler(self): '''Ensure we load the package set handler at the right time''' if not self._packageSetHandler: from oneconf.packagesethandler import PackageSetHandler, PackageSetInitError try: self._packageSetHandler = PackageSetHandler(self.hosts) except PackageSetInitError as e: LOG.error (e) self._packageSetHandler = None return self._packageSetHandler @dbus.service.method(HOSTS_INTERFACE) def get_all_hosts(self): self.activity = True return self.hosts.get_all_hosts() @dbus.service.method(HOSTS_INTERFACE) def set_share_inventory(self, share_inventory, hostid, hostname): self.activity = True if share_inventory: # map to boolean to avoid difference in dbus call and direct share_inventory = True else: share_inventory = False return self.hosts.set_share_inventory(share_inventory, hostid, hostname) @dbus.service.method(PACKAGE_SET_INTERFACE) def get_packages(self, hostid, hostname, only_manual): self.activity = True if not self.get_packageSetHandler(): return '' return none_to_null(self.get_packageSetHandler().get_packages(hostid, hostname, only_manual)) @dbus.service.method(PACKAGE_SET_INTERFACE) def diff(self, hostid, hostname): self.activity = True if not self.get_packageSetHandler(): return ('', '') return self.get_packageSetHandler().diff(hostid, hostname) @dbus.service.method(PACKAGE_SET_INTERFACE) def update(self): self.activity = True if self.get_packageSetHandler(): self.get_packageSetHandler().update() @dbus.service.method(PACKAGE_SET_INTERFACE) def async_update(self): self.activity = True if self.get_packageSetHandler(): GLib.timeout_add_seconds(1, self.get_packageSetHandler().update) @dbus.service.signal(HOSTS_INTERFACE) def hostlist_changed(self): LOG.debug("Send host list changed dbus signal") @dbus.service.signal(PACKAGE_SET_INTERFACE) def packagelist_changed(self, hostid): LOG.debug("Send package list changed dbus signal for hostid: %s" % hostid) @dbus.service.signal(HOSTS_INTERFACE) def logo_changed(self, hostid): LOG.debug("Send logo changed dbus signal for hostid: %s" % hostid) @dbus.service.signal(HOSTS_INTERFACE) def latestsync_changed(self, timestamp): LOG.debug("Send last sync timestamp: %s" % timestamp) @dbus.service.method(HOSTS_INTERFACE) def get_last_sync_date(self): self.activity = True return self.hosts.get_last_sync_date() @dbus.service.method(HOSTS_INTERFACE) def stop_service(self): LOG.debug("Request for stopping OneConf service") self.loop.quit() return True class DbusConnect(object): """ Dbus request sender, daemon connection """ def __init__(self): '''connect to the bus and get packagesethandler object''' self.bus = dbus.SessionBus() self.hosts_dbus_object = self.bus.get_object(ONECONF_SERVICE_NAME, HOSTS_OBJECT_NAME) def _get_package_handler_dbusobject(self): '''get package handler dbus object''' return dbus.Interface(self.hosts_dbus_object, PACKAGE_SET_INTERFACE) def _get_hosts_dbusobject(self): '''get hosts dbus object''' return dbus.Interface(self.hosts_dbus_object, HOSTS_INTERFACE) def get_all_hosts(self): '''get a dictionnary of all available hosts''' return self._get_hosts_dbusobject().get_all_hosts() def set_share_inventory(self, share_inventory, hostid='', hostname=''): '''update if we share the chosen host inventory on the server''' self._get_hosts_dbusobject().set_share_inventory(share_inventory, hostid, hostname, timeout=ONECONF_DBUS_TIMEOUT) def get_packages(self, hostid, hostname, only_manual): '''trigger getpackages handling''' try: return self._get_package_handler_dbusobject().get_packages(hostid, hostname, only_manual) except dbus.exceptions.DBusException as e: print(e) sys.exit(1) def diff(self, hostid, hostname): '''trigger diff handling''' try: return self._get_package_handler_dbusobject().diff(hostid, hostname, timeout=ONECONF_DBUS_TIMEOUT) except dbus.exceptions.DBusException as e: print(e) sys.exit(1) def update(self): '''trigger update handling''' self._get_package_handler_dbusobject().update(timeout=ONECONF_DBUS_TIMEOUT) def async_update(self): '''trigger update handling''' self._get_package_handler_dbusobject().async_update() def get_last_sync_date(self): '''just send a kindly ping to retrieve the last sync date''' return self._get_hosts_dbusobject().get_last_sync_date(timeout=ONECONF_DBUS_TIMEOUT) def stop_service(self): '''kindly ask the oneconf service to stop''' try: self._get_hosts_dbusobject().stop_service() except dbus.exceptions.DBusException as e: print(_("Wasn't able to request stopping the service: %s" % e)) sys.exit(1) oneconf-0.3.9/oneconf/networksync/0000775000000000000000000000000012624561776014077 5ustar oneconf-0.3.9/oneconf/networksync/__main__.py0000664000000000000000000000271712624560235016165 0ustar # -*- coding: utf-8 -*- # Copyright (C) 2011 Canonical # # Authors: # Didier Roche # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 3. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """For the test suite. See test/test_syncing.py """ import os import sys import logging from gi.repository import GLib from oneconf.paths import WEBCATALOG_SILO_SOURCE from . import SyncHandler from .infraclient_fake import WebCatalogAPI from ..hosts import Hosts if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) from dbus.mainloop.glib import DBusGMainLoop DBusGMainLoop(set_as_default=True) os.environ["ONECONF_SINGLE_SYNC"] = "True" infraclient = None if not "--no-infra-client" in sys.argv: infraclient = WebCatalogAPI(WEBCATALOG_SILO_SOURCE) sync_handler = SyncHandler(Hosts(), infraclient=infraclient) loop = GLib.MainLoop() GLib.timeout_add_seconds(15, loop.quit) loop.run() oneconf-0.3.9/oneconf/networksync/infraclient_fake.py0000664000000000000000000001472312624560235017731 0ustar """This module provides the RatingsAndReviewsAPI class for talking to the ratings and reviews API, plus a few helper classes. """ from piston_mini_client import ( PistonAPI, returns_json, ) from piston_mini_client.validators import validate_pattern from piston_mini_client.failhandlers import APIError # These are factored out as constants for if you need to work against a # server that doesn't support both schemes (like http-only dev servers) PUBLIC_API_SCHEME = 'http' AUTHENTICATED_API_SCHEME = 'https' from .fake_webcatalog_silo import FakeWebCatalogSilo, network_delay import os import json from oneconf.paths import WEBCATALOG_SILO_RESULT, WEBCATALOG_SILO_DIR class WebCatalogAPI(PistonAPI): """A fake client pretending to be WebCatalogAPI from infraclient_pristine. Uses settings from fake_webcatalog_settings to provide predictable responses to methods that try to use the WebCatalogAPI for testing purposes (i.e. without network activity). To use this, instead of importing from infraclient_pristine, you can import from infraclient_fake instead. """ default_service_root = 'http://localhost:8000/cat/api/1.0' default_content_type = 'application/x-www-form-urlencoded' _exception_msg = 'Fake WebCatalogAPI raising fake exception' def __init__(self, fake_settings_filename = None): super(WebCatalogAPI, self).__init__() self.silo = FakeWebCatalogSilo(fake_settings_filename) self.silo.save_settings(WEBCATALOG_SILO_RESULT) def machineuuid_exist(self, machine_uuid): '''Generic method to check before doing an update operation that the machine_uuid exist in the host list''' return (machine_uuid in self.silo.get_host_silo()) @returns_json @network_delay def server_status(self): if self.silo.get_setting('server_response_error'): raise APIError(self._exception_msg) return json.dumps('ok') @network_delay def list_machines(self): if self.silo.get_setting('list_machines_error'): raise APIError(self._exception_msg) dict_of_hosts = self.silo.get_setting('hosts_metadata') # the server is returning a list result = [] for hostid in dict_of_hosts: machine = dict_of_hosts[hostid] machine['uuid'] = hostid result.append(machine) return result @validate_pattern('machine_uuid', r'[-\w+]+') @validate_pattern('hostname', r'[-\.\w+]+') @returns_json @network_delay def update_machine(self, machine_uuid, hostname): if self.silo.get_setting('update_machine_error'): raise APIError(self._exception_msg) # update our content dictionnary with new data or creating a new entry hosts = self.silo.get_host_silo() if self.machineuuid_exist(machine_uuid): hosts[machine_uuid]['hostname'] = hostname else: hosts[machine_uuid] = {'hostname': hostname, 'logo_checksum': None, 'packages_checksum': None, } self.silo.save_settings(WEBCATALOG_SILO_RESULT) return json.dumps('Success') @validate_pattern('machine_uuid', r'[-\w+]+') @returns_json def delete_machine(self, machine_uuid): if self.silo.get_setting('delete_machine_error'): raise APIError(self._exception_msg) # delete the host if exist from the entry hosts = self.silo.get_host_silo() packages = self.silo.get_package_silo() try: del(packages[machine_uuid]) except KeyError: pass # there was no package list logo_path = os.path.join(WEBCATALOG_SILO_DIR, "%s.png" % machine_uuid) try: os.remove(logo_path) except OSError: pass # there was no logo if not self.machineuuid_exist(machine_uuid): raise APIError('Host Not Found') del hosts[machine_uuid] self.silo.save_settings(WEBCATALOG_SILO_RESULT) return json.dumps('Success') @validate_pattern('machine_uuid', r'[-\w+]+') def get_machine_logo(self, machine_uuid): if self.silo.get_setting('get_machine_logo_error'): raise APIError(self._exception_msg) logo_path = os.path.join(WEBCATALOG_SILO_DIR, "%s.png" % machine_uuid) if not os.path.exists(logo_path): raise APIError("No logo found") with open(logo_path) as fp: return fp.read() @validate_pattern('machine_uuid', r'[-\w+]+') @validate_pattern('logo_checksum', r'[-\w+]+\.[-\w+]+') @returns_json def update_machine_logo(self, machine_uuid, logo_checksum, logo_content): if self.silo.get_setting('update_machine_logo_error'): raise APIError(self._exception_msg) if not self.machineuuid_exist(machine_uuid): raise APIError('Host Not Found') image_path = os.path.join(WEBCATALOG_SILO_DIR,'%s.png' % machine_uuid) with open(image_path, 'wb+') as image_on_disk: image_on_disk.write(logo_content) hosts = self.silo.get_host_silo() hosts[machine_uuid]['logo_checksum'] = logo_checksum self.silo.save_settings(WEBCATALOG_SILO_RESULT) return json.dumps('Success') @validate_pattern('machine_uuid', r'[-\w+]+') @returns_json def list_packages(self, machine_uuid): if self.silo.get_setting('list_packages_error'): raise APIError(self._exception_msg) packages = self.silo.get_package_silo() if machine_uuid not in packages: raise APIError('Package list empty') package_list = packages[machine_uuid] if not package_list: raise APIError('Package list empty') return json.dumps(package_list) @validate_pattern('machine_uuid', r'[-\w+]+') @validate_pattern('packages_checksum', r'[-\w+]+') @returns_json def update_packages(self, machine_uuid, packages_checksum, package_list): if self.silo.get_setting('update_packages_error'): raise APIError(self._exception_msg) if not self.machineuuid_exist(machine_uuid): raise APIError('Host Not Found') packages = self.silo.get_package_silo() packages[machine_uuid] = package_list hosts = self.silo.get_host_silo() hosts[machine_uuid]['packages_checksum'] = packages_checksum self.silo.save_settings(WEBCATALOG_SILO_RESULT) return json.dumps('Success') oneconf-0.3.9/oneconf/networksync/infraclient_pristine.py0000664000000000000000000001024212624560235020650 0ustar """This module provides the WebCatalogAPI class for talking to the webcatalog API, plus a few helper classes. """ import ast import json from piston_mini_client import ( PistonAPI, returns_json ) from piston_mini_client.validators import ( oauth_protected, validate_pattern, ) from piston_mini_client.failhandlers import APIError # These are factored out as constants for if you need to work against a # server that doesn't support both schemes (like http-only dev servers) PUBLIC_API_SCHEME = 'http' AUTHENTICATED_API_SCHEME = 'https' class WebCatalogAPI(PistonAPI): """A client for talking to the webcatalog API. If you pass no arguments into the constructor it will try to connect to localhost:8000 so you probably want to at least pass in the ``service_root`` constructor argument. """ # default_service_root = 'http://localhost:8000/cat/api/1.0' default_service_root = 'https://apps.staging.ubuntu.com/cat/api/1.0' default_content_type = 'application/x-www-form-urlencoded' def _get(self, *args, **kwargs): """Return super's _get() as str, converting it from bytes if necessary.""" res = super()._get(*args, **kwargs) return res.decode() if type(res) == bytes else res @returns_json def server_status(self): """Check the state of the server, to see if everything's ok.""" return self._get('server-status/', scheme=PUBLIC_API_SCHEME) @oauth_protected def list_machines(self): """List all machine for the current user.""" return ast.literal_eval(self._get('list-machines/', scheme=AUTHENTICATED_API_SCHEME)) @validate_pattern('machine_uuid', r'[-\w+]+') @validate_pattern('hostname', r'[-\.\w+]+') @returns_json @oauth_protected def update_machine(self, machine_uuid, hostname): """Register or update an existing machine with new name.""" # fake logo_checksum for now data = {"hostname": hostname, "logo_checksum": "a"} return self._post('machine/%s/' % machine_uuid, data=data, scheme=AUTHENTICATED_API_SCHEME, content_type='application/json') @validate_pattern('machine_uuid', r'[-\w+]+') @returns_json @oauth_protected def delete_machine(self, machine_uuid): """Delete an existing machine.""" return self._delete('machine/%s/' % machine_uuid, scheme=AUTHENTICATED_API_SCHEME) @validate_pattern('machine_uuid', r'[-\w+]+') @oauth_protected def get_machine_logo(self, machine_uuid): """get the logo for a machine.""" return self._get('logo/%s/' % machine_uuid, scheme=AUTHENTICATED_API_SCHEME) @validate_pattern('machine_uuid', r'[-\w+]+') @validate_pattern('logo_checksum', r'[-\w+]+\.[-\w+]+') @returns_json @oauth_protected def update_machine_logo(self, machine_uuid, logo_checksum, logo_content): """update the logo for a machine.""" return self._post('logo/%s/%s/' % (machine_uuid, logo_checksum), data=logo_content, content_type='image/png', scheme=AUTHENTICATED_API_SCHEME) @validate_pattern('machine_uuid', r'[-\w+]+') @returns_json @oauth_protected def list_packages(self, machine_uuid): """List all packages for that machine""" package_list = self._get('packages/%s/' % machine_uuid, scheme=AUTHENTICATED_API_SCHEME) if not package_list: raise APIError('Package list empty') # FIXME: need to do this hack to transform the http request to a json format content try: package_list = ast.literal_eval(package_list[1:-1]) except ValueError as e: raise APIError('Package list invalid: %s' % e) return package_list @validate_pattern('machine_uuid', r'[-\w+]+') @validate_pattern('packages_checksum', r'[-\w+]+') @returns_json @oauth_protected def update_packages(self, machine_uuid, packages_checksum, package_list): """update the package list for a machine.""" data_content = {"package_list": package_list, "packages_checksum": packages_checksum} return self._post('packages/%s/' % machine_uuid, data=data_content, content_type='application/json', scheme=AUTHENTICATED_API_SCHEME) oneconf-0.3.9/oneconf/networksync/ssohandler.py0000664000000000000000000001016012624560235016576 0ustar #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010-2011 Canonical # # Authors: # Michael Vogt # Didier Roche # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 3. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import dbus import gettext from gi.repository import GObject, GLib import logging import os from oneconf.enums import MIN_TIME_WITHOUT_ACTIVITY #from gettext import gettext as _ gettext.textdomain("software-center") NO_OP = lambda *args, **kwargs: None LOG = logging.getLogger(__name__) class LoginBackendDbusSSO(GObject.GObject): __gsignals__ = { "login-result" : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, (GObject.TYPE_PYOBJECT,), ), } def __init__(self): super(LoginBackendDbusSSO, self).__init__() # use USC credential #self.appname = _("Ubuntu Software Center Store") self.appname = "Ubuntu One" if "ONECONF_SSO_CRED" in os.environ: if os.environ["ONECONF_SSO_CRED"].lower() == 'true': LOG.warn('forced fake sso cred...') GLib.idle_add(self._on_credentials_found, self.appname, "foo") else: LOG.warn('forced not having any sso cred...') GLib.idle_add(self._on_credentials_not_found, self.appname) return self.bus = dbus.SessionBus() self.proxy = None self._get_sso_proxy() # try it in a spawn/retry process to avoid ubuntu sso login issues GLib.timeout_add_seconds(MIN_TIME_WITHOUT_ACTIVITY, self._get_sso_proxy) def _get_sso_proxy(self): '''avoid crashing if ubuntu sso doesn't answer, which seems common''' LOG.debug("Try to get a proxy") try: # recreate a proxy object to respawn the sso daemon # (TODO: migration to gdbus and dbus owner changed should help) self.proxy = self.bus.get_object('com.ubuntu.sso', '/com/ubuntu/sso/credentials') self.proxy.connect_to_signal("CredentialsFound", self._on_credentials_found) self.proxy.connect_to_signal("CredentialsNotFound", self._on_credentials_not_found) self.proxy.connect_to_signal("CredentialsError", self._on_credentials_error) LOG.debug("look for credential") self.proxy.find_credentials(self.appname, '', reply_handler=NO_OP, error_handler=NO_OP) except dbus.DBusException as e: LOG.debug("No reply from ubuntu sso: %s" % e) return True # try again def _on_credentials_found(self, app_name, credentials): if app_name != self.appname: return LOG.debug("credential found") self.emit("login-result", credentials) def _on_credentials_not_found(self, app_name): if app_name != self.appname: return LOG.debug("credential not found") self.emit("login-result", None) def _on_credentials_error(self, app_name, error): if app_name != self.appname: return LOG.error("credential error") self.emit("login-result", None) if __name__ == "__main__": logging.basicConfig(level=logging.DEBUG) from dbus.mainloop.glib import DBusGMainLoop DBusGMainLoop(set_as_default=True) login = LoginBackendDbusSSO() loop = GLib.MainLoop() def print_result(obj, foo): print(foo) login.connect("login-result", print_result) loop.run() oneconf-0.3.9/oneconf/networksync/fake_webcatalog_silo.py0000664000000000000000000001343712624560235020572 0ustar import time import logging import os import pickle LOG = logging.getLogger(__name__) # decorator to add a fake network delay if set # in FakeReviewSettings.fake_network_delay def network_delay(fn): def slp(self, *args, **kwargs): #FIXME: CHECK how a decorator can take parameters #delay = fake_settings.get_setting('fake_network_delay') delay = 2 if delay: time.sleep(delay) return fn(self, *args, **kwargs) return slp class FakeWebCatalogSilo(object): """An object that simply holds settings and data which are used by WebCatalogAPI in the infraclient_fake module. Using this module allows a developer to test the oneconf functionality without any interaction with a webcatalog server. Each setting here provides complete control over how the 'server' will respond. Changes to these settings should be made to the class attributes directly without creating an instance of this class. The intended usage is for unit tests where a predictable response is required and where the application should THINK it has spoken to a server. The unit test would make changes to settings in this class before running the unit test. It also contains some data for integration test, faking a in memory WebCatalog server. """ _FAKE_SETTINGS = {} # Default stored data #_FAKE_SETTINGS['hosts_metadata'] = { # 'AAAAA': {'hostname': 'aaaaa', 'logo_checksum': 'logoAAAAA', 'packages_checksum': 'packageAAAAAA'}, # 'BBBBB': {'hostname': 'bbbbb', 'logo_checksum': 'logoBBBBB', 'packages_checksum': 'packageBBBBBB'},} #_FAKE_SETTINGS['packages_metadata'] = { # 'AAAAA': {'kiki': {'auto': False}, 'unity': {'auto': False}, # 'libFoo': {'auto': True}, 'libFool': {'auto': True}}, # 'BBBBB': {'kiki': {'auto': False}, 'gnome-panel': {'auto': False}, # 'libBar': {'auto': True}, 'libFool': {'auto': False}},} _FAKE_SETTINGS['hosts_metadata'] = {} _FAKE_SETTINGS['packages_metadata'] = {} # general settings # ***************************** # delay (in seconds) before returning from any of the fake cat methods # useful for emulating real network timings (use None for no delays) _FAKE_SETTINGS['fake_network_delay'] = 2 # server status # ***************************** # can be env variables as well like: ONECONF_server_response_error # raises APIError if True _FAKE_SETTINGS['server_response_error'] = False # list machines # ***************************** # raises APIError if True _FAKE_SETTINGS['list_machines_error'] = False # update machine # ***************************** # raises APIError if True _FAKE_SETTINGS['update_machine_error'] = False # delete machine # ***************************** # raises APIError if True _FAKE_SETTINGS['delete_machine_error'] = False # get machine logo # ***************************** # raises APIError if True _FAKE_SETTINGS['get_machine_logo_error'] = False # update machine logo # ***************************** # raises APIError if True _FAKE_SETTINGS['update_machine_logo_error'] = False # list packages # ***************************** # raises APIError if True _FAKE_SETTINGS['list_packages_error'] = False # update package list # ***************************** # raises APIError if True _FAKE_SETTINGS['update_packages_error'] = False def __init__(self, silo_filepath=None): """Initialises the object and loads the settings into the _FAKE_SETTINGS dict.. If settings_file is not provided, any existing settings in the cache file are ignored and the cache file is overwritten with the defaults set in the class. """ if silo_filepath: self._update_from_file(silo_filepath) def get_setting(self, key_name): """Takes a string (key_name) which corresponds to a setting in this object. Raises a NameError if the setting name doesn't exist """ if 'error' in key_name: value = os.getenv('ONECONF_' + key_name) # The value should be the string True or False, but it can be None. if value is not None: if value.lower() == 'true': return True elif value.lower() == 'false': return False else: raise RuntimeError('unexpected value %s' % value) if not key_name in self._FAKE_SETTINGS: raise NameError('Setting %s does not exist' % key_name) return self._FAKE_SETTINGS[key_name] def get_host_silo(self): """ return a reference to the host list silo""" return self._FAKE_SETTINGS['hosts_metadata'] def get_package_silo(self): """ return a reference to the package list silo""" return self._FAKE_SETTINGS['packages_metadata'] def _update_from_file(self, filepath): '''Loads existing settings from cache file into _FAKE_SETTINGS dict''' if os.path.exists(filepath): with open(filepath, 'rb') as fp: self._FAKE_SETTINGS = pickle.load(fp) else: LOG.warning("Settings file %s doesn't exist. " 'Will run with the default' % filepath) return def save_settings(self, filepath): """write the dict out to cache file, for generating new cases""" try: if not os.path.exists(os.path.dirname(filepath)): os.makedirs(os.path.dirname(filepath)) # File must be open in binary mode since pickle will write bytes. with open(filepath, 'wb') as fp: pickle.dump(self._FAKE_SETTINGS, fp) return True except: return False oneconf-0.3.9/oneconf/networksync/netstatus.py0000664000000000000000000001274312624560235016477 0ustar #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2011 Canonical # # Authors: # Matthew McGowan # Michael Vogt # Didier Roche # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 3. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import dbus from gi.repository import GObject, GLib import logging import os LOG = logging.getLogger(__name__) class NetworkStatusWatcher(GObject.GObject): """ simple watcher which notifys subscribers to network events...""" # enums for network manager status # Old enum values are for NM 0.7 # The NetworkManager daemon is in an unknown state. NM_STATE_UNKNOWN = 0 NM_STATE_UNKNOWN_LIST = [NM_STATE_UNKNOWN] # The NetworkManager daemon is asleep and all interfaces managed by it are inactive. NM_STATE_ASLEEP_OLD = 1 NM_STATE_ASLEEP = 10 NM_STATE_ASLEEP_LIST = [NM_STATE_ASLEEP_OLD, NM_STATE_ASLEEP] # The NetworkManager daemon is connecting a device. NM_STATE_CONNECTING_OLD = 2 NM_STATE_CONNECTING = 40 NM_STATE_CONNECTING_LIST = [NM_STATE_CONNECTING_OLD, NM_STATE_CONNECTING] # The NetworkManager daemon is connected. NM_STATE_CONNECTED_OLD = 3 NM_STATE_CONNECTED_LOCAL = 50 NM_STATE_CONNECTED_SITE = 60 NM_STATE_CONNECTED_GLOBAL = 70 NM_STATE_CONNECTED_LIST = [NM_STATE_CONNECTED_OLD, NM_STATE_CONNECTED_LOCAL, NM_STATE_CONNECTED_SITE, NM_STATE_CONNECTED_GLOBAL] # The NetworkManager daemon is disconnecting. NM_STATE_DISCONNECTING = 30 NM_STATE_DISCONNECTING_LIST = [NM_STATE_DISCONNECTING] # The NetworkManager daemon is disconnected. NM_STATE_DISCONNECTED_OLD = 4 NM_STATE_DISCONNECTED = 20 NM_STATE_DISCONNECTED_LIST = [NM_STATE_DISCONNECTED_OLD, NM_STATE_DISCONNECTED] __gsignals__ = {'changed':(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (bool,)), } def __init__(self): GObject.GObject.__init__(self) self.connected = False # check is ONECONF_NET_CONNECTED is in the environment variables # if so force the network status to be connected or disconnected if "ONECONF_NET_CONNECTED" in os.environ: if os.environ["ONECONF_NET_CONNECTED"].lower() == 'true': GLib.idle_add(self._on_connection_state_changed, self.NM_STATE_CONNECTED_LOCAL) LOG.warn('forced netstate into connected mode...') else: GLib.idle_add(self._on_connection_state_changed, self.NM_STATE_DISCONNECTED) LOG.warn('forced netstate into disconnected mode...') return try: bus = dbus.SystemBus() nm = bus.get_object('org.freedesktop.NetworkManager', '/org/freedesktop/NetworkManager') nm.connect_to_signal("StateChanged", self._on_connection_state_changed) network_state = nm.state(dbus_interface='org.freedesktop.NetworkManager') self._on_connection_state_changed(network_state) except Exception as e: LOG.warn("failed to init network state watcher '%s'" % e) self._on_connection_state_changed(self.NM_STATE_UNKNOWN) def _on_connection_state_changed(self, state): LOG.debug("network status changed to %i", state) # this is to avoid transient state when we turn wifi on and nm tell # "is connected" by default until checking GLib.timeout_add_seconds(1, self._ensure_new_connected_state, self._does_state_mean_connected(state)) def _ensure_new_connected_state(self, connected): '''check if the connectivity state changed since last check This is to avoid some transient state with nm flickering between connected and not connected''' if self.connected == connected: return self.connected = connected LOG.debug("Connectivity state changed to: %s", self.connected) self.emit("changed", self.connected) def _does_state_mean_connected(self, network_state): """ get bool if we the state means we are connected """ # unkown because in doubt, just assume we have network return network_state in self.NM_STATE_UNKNOWN_LIST + self.NM_STATE_CONNECTED_LIST if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) from dbus.mainloop.glib import DBusGMainLoop DBusGMainLoop(set_as_default=True) network = NetworkStatusWatcher() loop = GObject.MainLoop() def print_state(new_network, connected): print("Connectivity state: %s" % connected) network.connect("changed", print_state) loop.run() oneconf-0.3.9/oneconf/networksync/__init__.py0000664000000000000000000003776212624560235016214 0ustar # -*- coding: utf-8 -*- # Copyright (C) 2011 Canonical # # Authors: # Didier Roche # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 3. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from gi.repository import GObject, GLib import json import logging import os import time from oneconf.enums import MIN_TIME_WITHOUT_ACTIVITY from oneconf import utils from .netstatus import NetworkStatusWatcher from .ssohandler import LoginBackendDbusSSO from oneconf.paths import ( LAST_SYNC_DATE_FILENAME, ONECONF_CACHE_DIR, OTHER_HOST_FILENAME, PACKAGE_LIST_PREFIX, PENDING_UPLOAD_FILENAME) from piston_mini_client.failhandlers import APIError try: from http.client import BadStatusLine except ImportError: # Python 2 from httplib import BadStatusLine from httplib2 import socket, ServerNotFoundError, RedirectLimit LOG = logging.getLogger(__name__) class SyncHandler(GObject.GObject): '''Handle sync request with the server from the dbus service''' def __init__(self, hosts, package_handler=None, infraclient=None, dbusemitter=None): GObject.GObject.__init__(self) self._netstate = NetworkStatusWatcher() self._sso_login = LoginBackendDbusSSO() self._can_sync = False self.credential = None self.hosts = hosts self.infraclient = infraclient self.package_handler = package_handler if dbusemitter: self.emit_new_hostlist = dbusemitter.hostlist_changed self.emit_new_packagelist = dbusemitter.packagelist_changed self.emit_new_logo = dbusemitter.logo_changed self.emit_new_latestsync = dbusemitter.latestsync_changed self._netstate.connect("changed", self._network_state_changed) self._sso_login.connect("login-result", self._sso_login_result) def _refresh_can_sync(self): '''compute current syncable state before asking for refresh the value''' new_can_sync = (self.credential is not None) and (self.infraclient is not None) and self._netstate.connected if self._can_sync == new_can_sync: return self._can_sync = new_can_sync # we can now start syncing (as it's a new status), adding the timeout # TODO: self.infraclient should be built here if self._can_sync: self.process_sync() # adding the timeout only if we are not on a single sync if os.environ.get('ONECONF_SINGLE_SYNC') is None: GLib.timeout_add_seconds(MIN_TIME_WITHOUT_ACTIVITY, self.process_sync) def _sso_login_result(self, sso_login, credential): if credential == self.credential: return self.credential = credential # Prepare the authenticated infraclient if self.credential and not self.infraclient: from piston_mini_client.auth import OAuthAuthorizer from .infraclient_pristine import WebCatalogAPI from oneconf.distributor import get_distro distro = get_distro() # No update if not supported distribution if not distro: return service_root = distro.ONECONF_SERVER authorizer = OAuthAuthorizer(token_key=credential['token'], token_secret=credential['token_secret'], consumer_key=credential['consumer_key'], consumer_secret=credential['consumer_secret'], oauth_realm='Ubuntu One') self.infraclient = WebCatalogAPI(service_root=service_root, auth=authorizer) self._refresh_can_sync() def _network_state_changed(self, netstate, connected): self._refresh_can_sync() def check_if_refresh_needed(self, old_data, new_data, hostid, key): '''Return if data dictionnary needs to be refreshed''' need_refresh = False LOG.debug("Check if %s needs to be refreshed for %s" % (key, hostid)) try: if old_data[hostid]['%s_checksum' % key] != new_data[hostid]['%s_checksum' % key]: need_refresh = True except KeyError: # there was no old_data, if the new ones are not none, refresh if new_data[hostid]['%s_checksum' % key]: need_refresh = True if need_refresh: LOG.debug("Refresh new %s" % key) return need_refresh def check_if_push_needed(self, local_data, distant_data, key): '''Return if data dictionnary needs to be refreshed Contrary to refresh needed, we are sure that the host is registered. However the local checksum can be null, telling that no refresh is needed''' LOG.debug("Check if %s for current host need to be pushed to infra" % key) try: need_push = (local_data['%s_checksum' % key] and (local_data['%s_checksum' % key] != distant_data['%s_checksum' % key])) except KeyError: need_push = True if need_push: LOG.debug("Push new %s" % key) return need_push def emit_new_hostlist(self): '''this signal will be bound at init time''' LOG.warning("emit_new_hostlist not bound to anything") def emit_new_packagelist(self, hostid): '''this signal will be bound at init time''' LOG.warning("emit_new_packagelist(%s) not bound to anything" % hostid) def emit_new_logo(self, hostid): '''this signal will be bound at init time''' LOG.warning("emit_new_logo(%s) not bound to anything" % hostid) def emit_new_latestsync(self, timestamp): '''this signal will be bound at init time''' LOG.warning("emit_new_lastestsync(%s) not bound to anything" % timestamp) def process_sync(self): '''start syncing what's needed if can sync process sync can be either started directly, or when can_sync changed''' # we can't no more sync, removing the timeout if not self._can_sync: return False LOG.debug("Start processing sync") # Check server connection try: if self.infraclient.server_status() != 'ok': LOG.error("WebClient server answering but not available") return True except (APIError, socket.error, ValueError, ServerNotFoundError, BadStatusLine, RedirectLimit) as e: LOG.error ("WebClient server answer error: %s", e) return True # Try to do every other hosts pending changes first (we will get fresh # data then) try: pending_upload_filename = os.path.join( self.hosts.get_currenthost_dir(), PENDING_UPLOAD_FILENAME) with open(pending_upload_filename, 'r') as f: pending_changes = json.load(f) # We're going to mutate the dictionary inside the loop, so we need # to make a copy of the keys dictionary view. for hostid in list(pending_changes.keys()): # now do action depending on what needs to be refreshed try: # we can only remove distant machines for now, not # register new ones try: if not pending_changes[hostid].pop('share_inventory'): LOG.debug('Removing machine %s requested as a ' 'pending change' % hostid) self.infraclient.delete_machine( machine_uuid=hostid) except APIError as e: LOG.error("WebClient server doesn't want to remove " "hostid (%s): %s" % (hostid, e)) # append it again to be done pending_changes[hostid]['share_inventory'] = False except KeyError: pass # after all changes, is hostid still relevant? if not pending_changes[hostid]: pending_changes.pop(hostid) # no more change, remove the file if not pending_changes: LOG.debug( "No more pending changes remaining, removing the file") os.remove(pending_upload_filename) # update the remaining tasks else: utils.save_json_file_update( pending_upload_filename, pending_changes) except IOError: pass except ValueError: LOG.warning("The pending file is broken, ignoring") current_hostid = self.hosts.current_host['hostid'] old_hosts = self.hosts.other_hosts hostlist_changed = None packagelist_changed = [] logo_changed = [] # Get all machines try: full_hosts_list = self.infraclient.list_machines() except APIError as e: LOG.error("Invalid machine list from server, stopping sync: %s" % e) return True other_hosts = {} distant_current_host = {} for machine in full_hosts_list: hostid = machine.pop("uuid") if hostid != current_hostid: other_hosts[hostid] = machine else: distant_current_host = machine # now refresh packages list for every hosts for hostid in other_hosts: # init the list as the infra can not send it if not "packages_checksum" in other_hosts[hostid]: other_hosts[hostid]["packages_checksum"] = None packagelist_filename = os.path.join(self.hosts.get_currenthost_dir(), '%s_%s' % (PACKAGE_LIST_PREFIX, hostid)) if self.check_if_refresh_needed(old_hosts, other_hosts, hostid, 'packages'): try: new_package_list = self.infraclient.list_packages(machine_uuid=hostid) utils.save_json_file_update(packagelist_filename, new_package_list) # if already loaded, unload the package cache if self.package_handler: try: self.package_handler.package_list[hostid]['valid'] = False except KeyError: pass packagelist_changed.append(hostid) except APIError as e: LOG.error ("Invalid package data from server: %s", e) try: old_checksum = old_hosts[hostid]['packages_checksum'] except KeyError: old_checksum = None other_hosts[hostid]['packages_checksum'] = old_checksum # refresh the logo for every hosts as well # WORKING but not wanted on the isd side for now #if self.check_if_refresh_needed(old_hosts, other_hosts, hostid, 'logo'): # try: # logo_content = self.infraclient.get_machine_logo(machine_uuid=hostid) # logo_file = open(os.path.join(self.hosts.get_currenthost_dir(), "%s_%s.png" % (LOGO_PREFIX, hostid)), 'wb+') # logo_file.write(self.infraclient.get_machine_logo(machine_uuid=hostid)) # logo_file.close() # logo_changed.append(hostid) # except APIError, e: # LOG.error ("Invalid data from server: %s", e) # try: # old_checksum = old_hosts[hostid]['logo_checksum'] # except KeyError: # old_checksum = None # other_hosts[hostid]['logo_checksum'] = old_checksum # Now that the package list and logo are successfully downloaded, save # the hosts metadata there. This removes as well the remaining package list and logo LOG.debug("Check if other hosts metadata needs to be refreshed") if other_hosts != old_hosts: LOG.debug("Refresh new host") hostlist_changed = True other_host_filename = os.path.join(ONECONF_CACHE_DIR, current_hostid, OTHER_HOST_FILENAME) utils.save_json_file_update(other_host_filename, other_hosts) self.hosts.update_other_hosts() # now push current host if not self.hosts.current_host['share_inventory']: LOG.debug("Ensure that current host is not shared") try: self.infraclient.delete_machine(machine_uuid=current_hostid) except APIError as e: # just a debug message as it can be already not shared LOG.debug ("Can't delete current host from infra: %s" % e) else: LOG.debug("Push current host to infra now") # check if current host changed try: if self.hosts.current_host['hostname'] != distant_current_host['hostname']: try: self.infraclient.update_machine(machine_uuid=current_hostid, hostname=self.hosts.current_host['hostname']) LOG.debug ("Host data refreshed") except APIError as e: LOG.error ("Can't update machine: %s", e) except KeyError: try: self.infraclient.update_machine(machine_uuid=current_hostid, hostname=self.hosts.current_host['hostname']) LOG.debug ("New host registered done") distant_current_host = {'packages_checksum': None, 'logo_checksum': None} except APIError as e: LOG.error ("Can't register new host: %s", e) # local package list if self.check_if_push_needed(self.hosts.current_host, distant_current_host, 'packages'): local_packagelist_filename = os.path.join(self.hosts.get_currenthost_dir(), '%s_%s' % (PACKAGE_LIST_PREFIX, current_hostid)) try: with open(local_packagelist_filename, 'r') as f: self.infraclient.update_packages(machine_uuid=current_hostid, packages_checksum=self.hosts.current_host['packages_checksum'], package_list=json.load(f)) except (APIError, IOError) as e: LOG.error ("Can't push current package list: %s", e) # local logo # WORKING but not wanted on the isd side for now #if self.check_if_push_needed(self.hosts.current_host, distant_current_host, 'logo'): # logo_file = open(os.path.join(self.hosts.get_currenthost_dir(), "%s_%s.png" % (LOGO_PREFIX, current_hostid))).read() # try: # self.infraclient.update_machine_logo(machine_uuid=current_hostid, logo_checksum=self.hosts.current_host['logo_checksum'], logo_content=logo_file) # LOG.debug ("refresh done") # except APIError, e: # LOG.error ("Error while pushing current logo: %s", e) # write the last sync date timestamp = str(time.time()) content = {"last_sync": timestamp} utils.save_json_file_update(os.path.join(self.hosts.get_currenthost_dir(), LAST_SYNC_DATE_FILENAME), content) # send dbus signal if needed events (just now so that we don't block on remaining operations) if hostlist_changed: self.emit_new_hostlist() for hostid in packagelist_changed: self.emit_new_packagelist(hostid) for hostid in logo_changed: self.emit_new_logo(hostid) self.emit_new_latestsync(timestamp) # continue syncing in the main loop return True oneconf-0.3.9/oneconf/hosts.py0000664000000000000000000003220412624560235013211 0ustar # Copyright (C) 2010 Canonical # # Authors: # Didier Roche # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 3. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import hashlib import json import logging import os import platform import sys from gi.repository import Gio from gettext import gettext as _ LOG = logging.getLogger(__name__) from oneconf.paths import ( FAKE_WALLPAPER, FAKE_WALLPAPER_MTIME, HOST_DATA_FILENAME, LAST_SYNC_DATE_FILENAME, LOGO_BASE_FILENAME, LOGO_PREFIX, ONECONF_CACHE_DIR, OTHER_HOST_FILENAME, PACKAGE_LIST_PREFIX, PENDING_UPLOAD_FILENAME) from oneconf import utils class HostError(Exception): def __init__(self, message): self.message = message def __str__(self): return repr(self.message) class Hosts(object): """ Class to get hosts """ def __init__(self): '''initialize database This will register/update this host if not already done. ''' # create cache dir if doesn't exist if not os.path.isdir(ONECONF_CACHE_DIR): os.makedirs(ONECONF_CACHE_DIR) (logo_checksum, logo_path) = self._get_current_wallpaper_data() LOG.debug('LOGO %s: %s' % (logo_checksum, logo_path)) try: # faking this id for testing purpose. Format is hostid:hostname hostid, hostname = os.environ["ONECONF_HOST"].split(':') LOG.debug("Fake current hostid to %s and hostname to %s" % (hostid, hostname)) except KeyError: with open('/var/lib/dbus/machine-id') as fp: hostid = fp.read()[:-1] hostname = platform.node() self._host_file_dir = os.path.join(ONECONF_CACHE_DIR, hostid) try: file_path = os.path.join(self._host_file_dir, HOST_DATA_FILENAME) with open(file_path, 'r') as f: self.current_host = json.load(f) has_changed = False if hostname != self.current_host['hostname']: self.current_host['hostname'] = hostname has_changed = True if hostid != self.current_host['hostid']: self.current_host['hostid'] = hostid has_changed = True if logo_checksum != self.current_host['logo_checksum']: if self._create_logo(logo_path): self.current_host['logo_checksum'] = logo_checksum has_changed = True if has_changed: self.save_current_host() except (IOError, ValueError): self.current_host = { 'hostid': hostid, 'hostname': hostname, 'share_inventory': False, 'logo_checksum': logo_checksum, 'packages_checksum': None, } if not os.path.isdir(self._host_file_dir): os.mkdir(self._host_file_dir) if not self._create_logo(logo_path): self.current_host['logo_checksum'] = None self.save_current_host() self.other_hosts = None self.update_other_hosts() def _get_current_wallpaper_data(self): '''Get current wallpaper metadatas from store''' # TODO: add fake objects instead of introducing logic into the code # for testing. file_path = FAKE_WALLPAPER file_mtime = FAKE_WALLPAPER_MTIME if not file_path: settings = Gio.Settings.new("org.gnome.desktop.background") file_path = settings.get_string("picture-uri") if not file_path: return ('', '') file_path = file_path.replace("file://", "") try: if not file_mtime: file_mtime = str(os.stat(file_path).st_mtime) file_path_bytes = file_path.encode(sys.getfilesystemencoding()) logo_checksum = "%s%s" % ( hashlib.sha224(file_path_bytes).hexdigest(), file_mtime) except OSError: logo_checksum = None file_path = None return (logo_checksum, file_path) def _create_logo(self, wallpaper_path): '''create a logo from a wallpaper return True if succeeded''' # 2012-12-20 BAW: There is as yet no PIL for Python 3. This means we # actually can't enable PIL for Python 2 either because otherwise, we # can't write a test suite that succeeds for both versions. Currently # oneconf must be bilingual because Software Center imports oneconf, # and it is Python 2. (Getting Xapian ported, or switched to some # other Python 3 friendly search engine would solve *that* probably, # so I guess it's a race between Xapian and PIL.) return False ## if not wallpaper_path: ## return False ## try: ## # 2012-11-21 BAW: There is as yet no PIL for Python 3. ## from PIL import Image ## except ImportError: ## return False ## try: ## im = Image.open(LOGO_BASE_FILENAME) ## im2 = Image.open(wallpaper_path) ## im3 = im2.resize((42, 26), Image.BICUBIC) ## im.paste(im3, (3,3)) ## im.save(os.path.join(self._host_file_dir, "%s_%s.png" % (LOGO_PREFIX, self.current_host['hostid']))) ## return True ## except IOError as e: ## LOG.warning ("Cant create logo for %s: %s" % (wallpaper_path, e)) ## return False def update_other_hosts(self): '''Update all the other hosts from local store''' new_other_hosts = self._load_other_hosts() if self.other_hosts: for old_hostid in self.other_hosts: if old_hostid not in new_other_hosts: try: os.remove(os.path.join(self.get_currenthost_dir(), '%s_%s' % (PACKAGE_LIST_PREFIX, old_hostid))) except OSError: pass try: os.remove(os.path.join(self.get_currenthost_dir(), '%s_%s.png' % (LOGO_PREFIX, old_hostid))) except OSError: pass # TODO: remove rather with regexp in case of crash during upgrade, do not keep cruft self.other_hosts = new_other_hosts def _load_other_hosts(self): '''Load all other hosts from local store''' try: with open(os.path.join(self._host_file_dir, OTHER_HOST_FILENAME), 'r') as f: return json.load(f) except (IOError, TypeError, ValueError) as e: LOG.warning("Error in loading %s file: %s" % (OTHER_HOST_FILENAME, e)) return {} def save_current_host(self, arg=None): '''Save current host on disk''' LOG.debug("Save current host to disk") utils.save_json_file_update(os.path.join(self._host_file_dir, HOST_DATA_FILENAME), self.current_host) def add_hostid_pending_change(self, change): '''Pend a scheduled change for another host on disk change has a {hostid: {key: value, key2: value2}} format''' LOG.debug("Pend a change for another host on disk") try: with open(os.path.join(self._host_file_dir, PENDING_UPLOAD_FILENAME), 'r') as f: pending_changes = json.load(f) except (IOError, ValueError): pending_changes = {} # merge existing changes with new ones for hostid in change: if not hostid in pending_changes: pending_changes[hostid] = {} pending_changes[hostid].update(change[hostid]) utils.save_json_file_update(os.path.join(self._host_file_dir, PENDING_UPLOAD_FILENAME), pending_changes) def get_hostid_pending_change(self, hostid, attribute): '''Get the status if a pending change is in progress for an host Return None if nothing in progress''' try: with open(os.path.join(self._host_file_dir, PENDING_UPLOAD_FILENAME), 'r') as f: return json.load(f)[hostid][attribute] except (IOError, KeyError, ValueError): return None def gethost_by_id(self, hostid): '''Get host dictionnary by id Return: hostname can trigger HostError exception if no hostname found for this id ''' if hostid == self.current_host['hostid']: return self.current_host try: return self.other_hosts[hostid] except KeyError: raise HostError(_("No hostname registered for this id")) def _gethostid_by_name(self, hostname): '''Get hostid by hostname Return: hostid can trigger HostError exception unexisting hostname or multiple hostid for this hostname ''' LOG.debug("Get a hostid for %s", hostname) result_hostid = None if hostname == self.current_host['hostname']: result_hostid = self.current_host['hostid'] for hostid in self.other_hosts: if hostname == self.other_hosts[hostid]['hostname']: if not result_hostid: result_hostid = hostid else: raise HostError(_("Multiple hostid registered for this "\ "hostname. Use --list --host to get the hostid and "\ "use the --hostid option.")) if not result_hostid: raise HostError(_("No hostid registered for this hostname")) return result_hostid def get_hostid_from_context(self, hostid=None, hostname=None): '''get and check hostid if hostid and hostname are none, hostid is the current one Return: the corresponding hostid, raise an error if multiple hostid for an hostname ''' if not hostid and not hostname: hostid = self.current_host['hostid'] if hostid: # just checking if it exists self.gethost_by_id(hostid) hostid = hostid else: hostid = self._gethostid_by_name(hostname) return hostid def get_currenthost_dir(self): '''Get the oneconf current host directory''' return self._host_file_dir def get_all_hosts(self): '''Return a dictionnary of all hosts put in them as dict -> tuple for dbus connection''' LOG.debug("Request to compute an list of all hosts") result = { self.current_host['hostid']: ( True, self.current_host['hostname'], self.current_host['share_inventory']), } for hostid in self.other_hosts: result[hostid] = ( False, self.other_hosts[hostid]['hostname'], True) return result def set_share_inventory(self, share_inventory, hostid=None, hostname=None): '''Change if we share the current inventory to other hosts''' if hostid or hostname: hostid = self.get_hostid_from_context(hostid, hostname) if hostid and (hostid != self.current_host['hostid']): # do not update if there is already this pending change is already registered pending_change_scheduled = self.get_hostid_pending_change(hostid, 'share_inventory') if pending_change_scheduled != None: if share_inventory == pending_change_scheduled: return save_function = self.add_hostid_pending_change arg = {hostid: {'share_inventory': share_inventory}} msg = "Update share_inventory state for %s to %s" % (hostid, share_inventory) else: save_function = self.save_current_host arg = None msg = "Update current share_inventory state to %s" % share_inventory if self.current_host['share_inventory'] == share_inventory: return self.current_host['share_inventory'] = share_inventory LOG.debug(msg) save_function(arg) def get_last_sync_date(self): '''Get last sync date, if already synced, with remote server''' LOG.debug("Getting last sync date with remote server") try: with open(os.path.join(self._host_file_dir, LAST_SYNC_DATE_FILENAME), 'r') as f: content = json.load(f) last_sync = content['last_sync'] #last_sync = datetime.datetime.fromtimestamp(content['last_sync']).strftime("%X %x") except IOError: last_sync = _("Was never synced") # FIXME: give a better sentence like "Last sync not completed successfully", but let's not add a translation right now except ValueError: last_sync = _("Was never synced") return last_sync oneconf-0.3.9/oneconf/directconnect.py0000664000000000000000000000557112624560235014704 0ustar # Copyright (C) 2010 Canonical # # Authors: # Didier Roche # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 3. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from gettext import gettext as _ from oneconf.hosts import Hosts, HostError import sys class DirectConnect(object): """ Dummy backend handling exit and exception directly """ def _ensurePackageSetHandler(self): '''Ensure we import the package set handler at the right time''' from oneconf.packagesethandler import PackageSetHandler self.PackageSetHandler = PackageSetHandler def get_all_hosts(self): '''get a dict of all available hosts''' return Hosts().get_all_hosts() def set_share_inventory(self, share_inventory, hostid=None, hostname=None): '''update if we share the chosen host inventory on the server''' try: Hosts().set_share_inventory(share_inventory, hostid, hostname) except HostError as e: print(e) sys.exit(1) def get_packages(self, hostid, hostname, only_manual): '''trigger getpackages handling''' try: self._ensurePackageSetHandler() return self.PackageSetHandler().get_packages( hostid, hostname, only_manual) except HostError as e: print(e) sys.exit(1) def diff(self, hostid, hostname): '''trigger diff handling''' try: self._ensurePackageSetHandler() return self.PackageSetHandler().diff(hostid, hostname) except HostError as e: print(e) sys.exit(1) def update(self): '''trigger update handling''' try: self._ensurePackageSetHandler() self.PackageSetHandler().update() except HostError as e: print(e) sys.exit(1) def async_update(self): '''only used in fallback mode: no async notion for direct connexion''' self.update() def get_last_sync_date(self): '''get last time the store was successfully synced''' return Hosts().get_last_sync_date() def stop_service(self): '''kindly ask the oneconf service to stop (not relevant for a direct mode)''' print(_("Nothing done: in direct mode, there is no communication with the service")) sys.exit(1) oneconf-0.3.9/oneconf/__init__.py0000664000000000000000000000000012624560235013575 0ustar oneconf-0.3.9/oneconf/version.py0000664000000000000000000000010112624560235013525 0ustar VERSION='0.3' CODENAME='raring' DISTRO='Ubuntu' RELEASE='13.04' oneconf-0.3.9/oneconf/distributor/0000775000000000000000000000000012624561776014063 5ustar oneconf-0.3.9/oneconf/distributor/__init__.py0000664000000000000000000000441312624560235016163 0ustar # Copyright (C) 2009-2010 Canonical # # Authors: # Michael Vogt # Didier Roche # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 3. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import logging import subprocess try: from configparser import NoSectionError, RawConfigParser except ImportError: # Python 2 from ConfigParser import NoSectionError, RawConfigParser from importlib import import_module from oneconf.paths import ONECONF_OVERRIDE_FILE LOG = logging.getLogger(__name__) class Distro(object): """ abstract base class for a distribution """ def compute_local_packagelist(self): '''Introspect what's installed on this hostid Return: installed_packages list ''' raise NotImplementedError def _get_distro(): config = RawConfigParser() try: config.read(ONECONF_OVERRIDE_FILE) distro_id = config.get('TestSuite', 'distro') except NoSectionError: distro_id = subprocess.Popen( ["lsb_release","-i","-s"], stdout=subprocess.PIPE, universal_newlines=True).communicate()[0].strip() LOG.debug("get_distro: '%s'" % distro_id) # start with a import, this gives us only a oneconf module try: module = import_module('.' + distro_id, 'oneconf.distributor') # get the right class and instanciate it distro_class = getattr(module, distro_id) instance = distro_class() except ImportError: LOG.warn("invalid distro: '%s'" % distro_id) return None return instance def get_distro(): """ factory to return the right Distro object """ return distro_instance # singleton distro_instance=_get_distro() if __name__ == "__main__": print(get_distro()) oneconf-0.3.9/oneconf/distributor/Test.py0000664000000000000000000000222412624560235015341 0ustar # Copyright (C) 2011 Canonical # # Authors: # Didier Roche # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 3. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import logging LOG = logging.getLogger(__name__) from oneconf.distributor import Distro class Test(Distro): ONECONF_SERVER = "foo" def compute_local_packagelist(self): '''Introspect what's installed on this hostid Return: installed_packages list ''' LOG.debug ('Compute package list for current host') return {'foo': {"auto": False}, 'pool': {"auto": True}, } oneconf-0.3.9/oneconf/distributor/Ubuntu.py0000664000000000000000000000264012624560235015706 0ustar # Copyright (C) 2010 Canonical # # Authors: # Didier Roche # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 3. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import apt import logging LOG = logging.getLogger(__name__) from oneconf.distributor import Distro class Ubuntu(Distro): ONECONF_SERVER = "https://apps.ubuntu.com/cat/api/1.0" def compute_local_packagelist(self): '''Introspect what's installed on this hostid Return: installed_packages list ''' LOG.debug ('Compute package list for current host') # get list of all apps installed installed_packages = {} with apt.Cache() as apt_cache: for pkg in apt_cache: if pkg.is_installed: installed_packages[pkg.name] = {"auto": pkg.is_auto_installed} return installed_packages oneconf-0.3.9/oneconf-service0000775000000000000000000001014212624560235013060 0ustar #!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (C) 2010 Canonical # # Authors: # Didier Roche # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 3. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import dbus import logging from optparse import OptionParser import os import sys import gettext from gettext import gettext as _ LOG = logging.getLogger(__name__) from oneconf.enums import MIN_TIME_WITHOUT_ACTIVITY, ONECONF_SERVICE_NAME from oneconf.version import * def quit(loop, myservice): '''quit if no activity at least in MIN_TIME_WITHOUT_ACTIVITYs''' if myservice.activity: LOG.debug('Some recent activity, still alive for %ss' % MIN_TIME_WITHOUT_ACTIVITY) # TODO: check if server has new sync info and send a dbus signal there myservice.activity = False return True LOG.debug('No more activity, go to sleep') loop.quit() def createcheckforquit(loop, myservice): LOG.debug('Setting loop quit ping: %ss' % MIN_TIME_WITHOUT_ACTIVITY) # This function add 10 seconds more than in the software-center oneconf # plugins after starting up to keep the service up when USC is running # (there is a ping every MIN_TIME_WITHOUT_ACTIVITY - 10) GLib.timeout_add_seconds(MIN_TIME_WITHOUT_ACTIVITY, quit, loop, myservice) return False def createsynchandler(myservice, use_mock): # create only on demand to avoid first sync/upload to delay first request # this sync automatically as soon as network/sso is available LOG.debug('Create a sync handler with infra') infra = None if use_mock: from oneconf.networksync.infraclient_fake import WebCatalogAPI infra = WebCatalogAPI() myservice.synchandler = SyncHandler( myservice.hosts, package_handler=myservice.get_packageSetHandler(), infraclient=infra, dbusemitter=myservice) return False if __name__ == '__main__': usage = _("Usage: %prog [options]") parser = OptionParser(version= "%prog " + VERSION, usage=usage) parser.add_option("--debug", action="store_true", dest="debug", help=_("Enable debug mode.")) parser.add_option("--mock", action="store_true", dest="mock", help=_("Use the mock infrastructure.")) (options, args) = parser.parse_args() # don't run as root: if os.getuid() == 0: print("oneconf-service can't run as root. Exiting") sys.exit(1) # set verbosity if options.debug: logging.basicConfig(level=logging.DEBUG) else: logging.basicConfig(level=logging.INFO) # do import only now (otherwise, the import of dbus change level of debug) from gi.repository import GObject, GLib from dbus.mainloop.glib import DBusGMainLoop from oneconf.dbusconnect import DbusHostsService from oneconf.networksync import SyncHandler DBusGMainLoop(set_as_default=True) loop = GLib.MainLoop() # only OneConf service at a time error_message = None try: if ONECONF_SERVICE_NAME in dbus.SessionBus().list_names(): error_message =_("An OneConf service is already running, " "shut it down with oneconf-query --stop") else: myservice = DbusHostsService(loop) except dbus.DBusException as e: error_message = e if error_message: LOG.critical(error_message) sys.exit(1) GLib.timeout_add_seconds(10, createcheckforquit, loop, myservice) GLib.timeout_add_seconds(20, createsynchandler, myservice, options.mock) LOG.debug("daemon up and running") loop.run() oneconf-0.3.9/misc/0000775000000000000000000000000012624561776011015 5ustar oneconf-0.3.9/misc/oneconf-update0000775000000000000000000000031412624560235013635 0ustar #!/bin/sh # for trunk (handle symlink too) real_script=`readlink $0` [ -x "`dirname $real_script`/../oneconf-query" ] && PREFIX="`dirname $real_script`/../" ${PREFIX}oneconf-query --async-update exit $? oneconf-0.3.9/misc/com.ubuntu.OneConf.service0000664000000000000000000000011712624560235016010 0ustar [D-BUS Service] Name=com.ubuntu.OneConf Exec=/usr/share/oneconf/oneconf-serviceoneconf-0.3.9/data/0000775000000000000000000000000012624561776010773 5ustar oneconf-0.3.9/data/images/0000775000000000000000000000000012624561776012240 5ustar oneconf-0.3.9/data/images/computer.png0000664000000000000000000000535212624560235014576 0ustar PNG  IHDR00WsBIT|d IDATh՘UՕ?ks} #yq&2 SMJk0Mi&5&մ6ɄL&1B(tR0yՌPKX޳{s}Bv'}=wwZk-;w} EQ|0/_>>muADD4|dRk.r/gNVh Q% Z%G^ =%I'$5VJz l\n<ٛ0jY0gSNՆqzJ.F{:EDK99)AXVE^_(TtY_o,ܰ}XqD Jđ j`Ɗ4c uY8&yXui݈6+ Uf91f1l ЕH |iT;upI} ce5qS-b-m'-=A/Y=X 46(wZqT-^U3'cujtIh63de޾fHJN+I * `jj ZN0@M~pScez^O2`W꓉:)hyin *XkPlpo_&z)TrHsQH QxЯ"F*n\/IuM:=ܹ8lD$eB"?Ŵ&C߻1e۷'N,YrY曾tSlWoѮ䤝:u:9s挷yxY J}zgQD^Xx9y{z5k^0 =cHkKΜ9cnw||yor//i`.)0oVZ՚?>vvMբnSE}hlfeIۥviZZؗ>NZm]UٳgmyzgϞ}J0\NV Y5$2$4޽{?&"wy[lioڴ]7oO<m:8իy;;.zGN嫇8wCCC\p(!o|#GQՏ[Ng 70߿Eg??#͝'stt,V(Ɵtx^˹sطxdڃxGٵk״W a\wusy6TիWw~+WT`bji1F^z%{:)~}" 7u]w \i)"_~o^L>elo^Z?/BIENDB`oneconf-0.3.9/oneconf-query0000775000000000000000000002763112624560235012600 0ustar #!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (C) 2010 Canonical # # Authors: # Didier Roche # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 3. # # This program is distributed in the hope that it will be useful, but WITHOUTa # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import logging from optparse import OptionParser, OptionGroup import os import sys from time import localtime, strftime import gettext # Python 3 does not accept a 'unicode' argument, but _() will always return a # string (i.e. a unicode) anyway. gettext.install('oneconf') LOG = logging.getLogger(__name__) from oneconf.version import * SCOPE_NONE, SCOPE_ALL_PACKAGES, SCOPE_MANUAL_PACKAGES, SCOPE_HOSTS, SCOPE_HOST = range(5) (ACTION_NONE, ACTION_LIST, ACTION_DIFF, ACTION_UPDATE, ACTION_ASYNC_UPDATE, ACTION_SHARE_INVENTORY, ACTION_GET_LAST_SYNC, ACTION_STOP_SERVICE) = range(8) def print_packages(installed_pkg): print(_("Installed package:")) for pkg_name in installed_pkg: print(pkg_name) def print_packages_diff(packages_to_install, packages_to_remove): print(_("Additional packages: (package to install)")) for pkg_name in packages_to_install: print(" %s" % pkg_name) print(_("Missing packages: (package to remove)")) for pkg_name in packages_to_remove: print(" %s" % pkg_name) def print_hosts(hosts, only_current=False): if len(hosts) == 1 or only_current: print(_("Listing this host stored in OneConf:")) else: print(_("Hosts stored for OneConf:")) for hostid in hosts: current, name, share_inventory = hosts[hostid] additional_text = "" if current: additional_text = "[Current]" if (only_current and current) or not only_current: print("ID: %s %s\n name: %s\n share inventory: %s" % (hostid, additional_text, name, share_inventory)) def err_scope(): print(_("You can't define --all-packages, --manual-packages or --hosts together.")) sys.exit(1) def err_action(): print(_("You can't define --list, --diff, --update, --async-update, --share-inventory, --stop, --get-last-sync together.")) sys.exit(1) def option_not_compatible(options, action): print(_("%s isn't compatible with %s" % (options, action))) sys.exit(1) if __name__ == '__main__': # TODO: choose the future, choose argparse! usage = _("usage: %prog [options]") parser = OptionParser(version= "%prog " + VERSION, usage=usage) parser.add_option("-d", "--diff", action="store_true", dest="action_diff", help=_("Current diff between this machine and another " \ "provided by hostname/hostid")) parser.add_option("-l", "--list", action="store_true", dest="action_list", help=_("List stored package (default for local hostid) or host lists")) parser.add_option("--get-last-sync", action="store_true", dest="action_getlastsync", help=_("Get last sync date")) parser.add_option("-u", "--update", action="store_true", dest="action_update", help=_("Update the package list in store")) parser.add_option("--async-update", action="store_true", dest="action_async_update", help=_("Perform async update of the package list in store")) parser.add_option("--stop", action="store_true", dest="action_stopservice", help=_("Stop oneconf service")) parser.add_option("--debug", action="store_true", dest="debug", help=_("Enable debug mode (use --direct)")) parser.add_option("--direct", action="store_true", dest="directaccess", help=_("Don't use dbus for the request")) scope_group = OptionGroup(parser, "Scope of actions:", "This define the " \ "scope to consider for list and diff command.") scope_group.add_option("--all-packages", action="store_true", dest="scope_all_packages", help=_("Get all installed packages from storage")) scope_group.add_option("--manual-packages", action="store_true", dest="scope_manual_packages", help=_("Get only manual installed packages from storage")) scope_group.add_option("--hosts", action="store_true", dest="scope_hosts", help=_("All available hosts from storage (only with list)")) scope_group.add_option("--host", action="store_true", dest="scope_host", help=_("This host (only with list)")) scope_hosts = OptionGroup(parser, "Host scope:", "Thoses options " \ "can't be used together and only concerns diff and " \ "list actions. List hosts to get registered strings.") # default is '' for dbus compatible format scope_hosts.add_option("--hostname", action="store", dest="hostname", help=_("Specify target hostname"), default='') scope_hosts.add_option("--hostid", action="store", dest="hostid", help=_("Specify target hostid"), default='') scope_manage_host = OptionGroup(parser, "host management:", "Those options can't be used with anything else and are " "present to manage host parameters.") scope_manage_host.add_option("--share-inventory", action="store_true", dest="share_inventory", help=_("Share this inventory on the web"), default=None) scope_manage_host.add_option("--hide-inventory", action="store_false", dest="share_inventory", help=_("Hide this inventory on the web"), default=None) parser.add_option_group(scope_group) parser.add_option_group(scope_hosts) parser.add_option_group(scope_manage_host) (options, args) = parser.parse_args() # don't run as root: if os.getuid() == 0: print("oneconf-query can't run as root. Exiting") sys.exit(1) # set verbosity if options.debug: logging.basicConfig(level=logging.DEBUG) else: logging.basicConfig(level=logging.INFO) if options.directaccess or options.debug: LOG.debug("Direct call: only take from cache") from oneconf.directconnect import DirectConnect oneconf = DirectConnect() else: LOG.debug("Using dbus") from dbus import DBusException try: from oneconf.dbusconnect import DbusConnect oneconf = DbusConnect() except DBusException as e: LOG.critical("Can't connect to dbus: %s, fallback to direct connexion as a fallback:" % e) from oneconf.directconnect import DirectConnect oneconf = DirectConnect() # store_const doesn't handle conflicts, so use manual triage scope = SCOPE_NONE if options.scope_manual_packages: scope = SCOPE_MANUAL_PACKAGES if options.scope_all_packages: if scope != SCOPE_NONE: err_scope() scope = SCOPE_ALL_PACKAGES if options.scope_hosts: if scope != SCOPE_NONE: err_scope() scope = SCOPE_HOSTS if options.scope_host: if scope != SCOPE_NONE: err_scope() scope = SCOPE_HOST # store_const doesn't handle conflicts, so use manual triage action = ACTION_NONE if options.action_list: action = ACTION_LIST if options.action_diff: if action != ACTION_NONE: err_action() action = ACTION_DIFF if options.action_update: if action != ACTION_NONE: err_action() action = ACTION_UPDATE if options.action_async_update: if action != ACTION_NONE: err_action() action = ACTION_ASYNC_UPDATE if options.share_inventory is not None: # True and False both used if action != ACTION_NONE: err_action() action = ACTION_SHARE_INVENTORY if options.action_getlastsync: if action != ACTION_NONE: err_action() action = ACTION_GET_LAST_SYNC if options.action_stopservice: if action != ACTION_NONE: err_action() action = ACTION_STOP_SERVICE if action == ACTION_NONE: action = ACTION_LIST if options.hostid and options.hostname: print(_("hostid and hostname can't be provided together.")) sys.exit(1) if action == ACTION_UPDATE: if options.hostid or options.hostname: print(_("You can't use hostid or hostname when updating.")) sys.exit(1) if scope != SCOPE_NONE: print(_("You can't define --package, --host or --hosts " "when updating.")) sys.exit(1) oneconf.update() elif action == ACTION_ASYNC_UPDATE: if options.hostid or options.hostname: print(_("You can't use hostid or hostname when updating.")) sys.exit(1) if scope != SCOPE_NONE: print(_("You can't define --package, --host or --hosts " "when updating.")) sys.exit(1) oneconf.async_update() elif action == ACTION_LIST: if scope == SCOPE_NONE: scope = SCOPE_ALL_PACKAGES if scope == SCOPE_HOSTS: print_hosts(oneconf.get_all_hosts()) if scope == SCOPE_HOST: print_hosts(oneconf.get_all_hosts(), True) if scope == SCOPE_MANUAL_PACKAGES: installed_pkg = oneconf.get_packages(hostid=options.hostid, hostname=options.hostname, only_manual=True) print_packages(installed_pkg) if scope == SCOPE_ALL_PACKAGES: installed_pkg = oneconf.get_packages(hostid=options.hostid, hostname=options.hostname, only_manual=False) print_packages(installed_pkg) elif action == ACTION_DIFF: if not options.hostid and not options.hostname: print(_("You have to provide either hostid or hostname for " "getting a diff.")) sys.exit(1) if scope == SCOPE_NONE: scope = SCOPE_ALL_PACKAGES if scope == SCOPE_HOSTS: option_not_compatible("--hosts", "--diff") if scope == SCOPE_HOST: option_not_compatible("--host", "--diff") if scope == SCOPE_MANUAL_PACKAGES: option_not_compatible("--manual-packages", "--diff") if scope == SCOPE_ALL_PACKAGES: (packages_to_install, packages_to_remove) = oneconf.diff( hostid=options.hostid, hostname=options.hostname) print_packages_diff(packages_to_install, packages_to_remove) elif action == ACTION_SHARE_INVENTORY: if scope != SCOPE_NONE: print(_("You can't define --package, --host or --hosts " "when changing show inventory status.")) oneconf.set_share_inventory(options.share_inventory, options.hostid, options.hostname) elif action == ACTION_GET_LAST_SYNC: if options.hostid or options.hostname: print(_("You can't use hostid or hostname when changing show inventory status.")) sys.exit(1) if scope != SCOPE_NONE: print(_("You can't define --package, --host or --hosts " "when changing show inventory status.")) print(oneconf.get_last_sync_date()) elif action == ACTION_STOP_SERVICE: oneconf.stop_service() sys.exit(0)