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)
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._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 if self._can_sync: self.process_sync() # adding the timeout only if we are not on a single sync if not "ONECONF_SINGLE_SYNC" in os.environ or not os.environ["ONECONF_SINGLE_SYNC"]: GObject.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 service_root = get_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 Software Center') 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), 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) for hostid in 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, e: LOG.error("WebClient server doesn't want to remove hostid (%s): %s" % (hostid, e)) pending_changes[hostid]['share_inventory'] = False # append it again to be done 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, e: LOG.error ("Invalid machine list from server, stopping sync: %s" % e) return True