Exemple #1
0
 def newfunc(*args, **kwargs):
     result = None
     attempt = 0
     # setup the caller object
     iKwargs = {"debug": debug, "fd": fd, "verbose": verbose, "defaults": defaults, "trap": trap}
     caller = _InspectorGadget(func, **iKwargs)
     while attempt <= maximum:
         try:  # initial caller setup
             result = caller(*args, **kwargs)
             if isinstance(result, defer.Deferred):
                 return _deferred(result, caller)
             break  # reset data
         except SystemExit:
             result = Failure()
             break
         except KeyboardInterrupt:
             result = Failure()
             break
         except:
             attempt += 1
             if attempt > maximum:
                 result = Failure()
                 break
             time.sleep(delay)
             caller.write(">>> Retry attempt %d" % attempt)
     if isinstance(result, Failure):
         result.raiseException()
     return result
 def migrateOneHome(self, fileTxn, homeType, fileHome):
     """
     Migrate an individual calendar or addressbook home.
     """
     migrateFunc, destFunc = homeTypeLookup.get(homeType)
     uid = normalizeUUIDOrNot(fileHome.uid())
     self.log.warn("Starting migration transaction %s UID %r" %
                   (homeType, uid))
     sqlTxn = self.sqlStore.newTransaction(
         label="UpgradeToDatabaseStep.migrateOneHome")
     homeGetter = destFunc(sqlTxn)
     sqlHome = yield homeGetter(uid, create=False)
     if sqlHome is not None and not self.merge:
         self.log.warn("%s home %r already existed not migrating" %
                       (homeType, uid))
         yield sqlTxn.abort()
         returnValue(None)
     try:
         if sqlHome is None:
             sqlHome = yield homeGetter(uid, create=True)
         yield migrateFunc(fileHome, sqlHome, merge=self.merge)
     except:
         f = Failure()
         yield sqlTxn.abort()
         f.raiseException()
     else:
         yield sqlTxn.commit()
         # Remove file home after migration. FIXME: instead, this should be a
         # public remove...HomeWithUID() API for de-provisioning.  (If we had
         # this, this would simply be a store-to-store migrator rather than a
         # filesystem-to-database upgrade.)
         fileHome._path.remove()
 def migrateOneHome(self, fileTxn, homeType, fileHome):
     """
     Migrate an individual calendar or addressbook home.
     """
     migrateFunc, destFunc = homeTypeLookup.get(homeType)
     uid = normalizeUUIDOrNot(fileHome.uid())
     self.log.warn("Starting migration transaction %s UID %r" %
                   (homeType, uid))
     sqlTxn = self.sqlStore.newTransaction()
     homeGetter = destFunc(sqlTxn)
     sqlHome = yield homeGetter(uid, create=False)
     if sqlHome is not None and not self.merge:
         self.log.warn(
             "%s home %r already existed not migrating" % (
                 homeType, uid))
         yield sqlTxn.abort()
         returnValue(None)
     try:
         if sqlHome is None:
             sqlHome = yield homeGetter(uid, create=True)
         yield migrateFunc(fileHome, sqlHome, merge=self.merge)
     except:
         f = Failure()
         yield sqlTxn.abort()
         f.raiseException()
     else:
         yield sqlTxn.commit()
         # Remove file home after migration. FIXME: instead, this should be a
         # public remove...HomeWithUID() API for de-provisioning.  (If we had
         # this, this would simply be a store-to-store migrator rather than a
         # filesystem-to-database upgrade.)
         fileHome._path.remove()
    def http_POST(self, request):
        """
        The server-to-server POST method.
        """

        # Need a transaction to work with
        txn = transactionFromRequest(request, self._newStore)

        # This is a server-to-server scheduling operation.
        scheduler = IScheduleScheduler(txn, None, podding=self._podding)

        originator = self.loadOriginatorFromRequestHeaders(request)
        recipients = self.loadRecipientsFromRequestHeaders(request)
        body = (yield allDataFromStream(request.stream))

        # Do the POST processing treating this as a non-local schedule
        try:
            result = (yield scheduler.doSchedulingViaPOST(request.remoteAddr, request.headers, body, originator, recipients))
        except Exception:
            ex = Failure()
            yield txn.abort()
            ex.raiseException()
        else:
            yield txn.commit()
        response = result.response()
        if not self._podding:
            response.headers.addRawHeader(ISCHEDULE_CAPABILITIES, str(config.Scheduling.iSchedule.SerialNumber))
        returnValue(response)
Exemple #5
0
 def newfunc(*args, **kwargs):
     result = None
     attempt = 0
     #setup the caller object
     iKwargs = {
         'debug': debug,
         'fd': fd,
         'verbose': verbose,
         'defaults': defaults,
         'trap': trap
     }
     caller = _InspectorGadget(func, **iKwargs)
     while attempt <= maximum:
         try:  #initial caller setup
             result = caller(*args, **kwargs)
             if isinstance(result, defer.Deferred):
                 return _deferred(result, caller)
             break  #reset data
         except SystemExit:
             result = Failure()
             break
         except KeyboardInterrupt:
             result = Failure()
             break
         except:
             attempt += 1
             if attempt > maximum:
                 result = Failure()
                 break
             time.sleep(delay)
             caller.write('>>> Retry attempt %d' % attempt)
     if isinstance(result, Failure):
         result.raiseException()
     return result
Exemple #6
0
def doToEachHomeNotAtVersion(store, homeSchema, version, doIt, logStr, filterOwnerUID=None, processExternal=False):
    """
    Do something to each home whose version column indicates it is older
    than the specified version. Do this in batches as there may be a lot of work to do. Also,
    allow the GUID to be filtered to support a parallel mode of operation.
    """

    txn = store.newTransaction("updateDataVersion")
    where = homeSchema.DATAVERSION < version
    if filterOwnerUID:
        where = where.And(homeSchema.OWNER_UID.StartsWith(filterOwnerUID))
    total = (yield Select(
        [Count(homeSchema.RESOURCE_ID), ],
        From=homeSchema,
        Where=where,
    ).on(txn))[0][0]
    yield txn.commit()
    count = 0

    while True:

        logUpgradeStatus(logStr, count, total)

        # Get the next home with an old version
        txn = store.newTransaction("updateDataVersion")
        try:
            rows = yield Select(
                [homeSchema.RESOURCE_ID, homeSchema.OWNER_UID, homeSchema.STATUS, ],
                From=homeSchema,
                Where=where,
                OrderBy=homeSchema.OWNER_UID,
                Limit=1,
            ).on(txn)

            if len(rows) == 0:
                yield txn.commit()
                logUpgradeStatus("End {}".format(logStr), count, total)
                returnValue(None)

            # Apply to the home if not external
            homeResourceID, _ignore_owner_uid, homeStatus = rows[0]
            if homeStatus != _HOME_STATUS_EXTERNAL or processExternal:
                yield doIt(txn, homeResourceID)

            # Update the home to the current version
            yield Update(
                {homeSchema.DATAVERSION: version},
                Where=homeSchema.RESOURCE_ID == homeResourceID,
            ).on(txn)
            yield txn.commit()
        except RuntimeError, e:
            f = Failure()
            logUpgradeError(
                logStr,
                "Failed to upgrade {} to {}: {}".format(homeSchema, version, e)
            )
            yield txn.abort()
            f.raiseException()

        count += 1
def moveCalendarTimezoneProperties(sqlStore):
    """
    Need to move all the CalDAV:calendar-timezone properties in the
    RESOURCE_PROPERTY table to the new CALENDAR_BIND table columns, extracting
    the new value from the XML property.
    """

    cb = schema.CALENDAR_BIND
    rp = schema.RESOURCE_PROPERTY

    try:
        calendars_for_id = {}
        while True:
            sqlTxn = sqlStore.newTransaction()
            rows = (yield rowsForProperty(sqlTxn, caldavxml.CalendarTimeZone, with_uid=True, batch=BATCH_SIZE))
            if len(rows) == 0:
                yield sqlTxn.commit()
                break
            delete_ids = []
            for calendar_rid, value, viewer in rows:
                delete_ids.append(calendar_rid)
                if calendar_rid not in calendars_for_id:
                    ids = yield Select(
                        [cb.CALENDAR_HOME_RESOURCE_ID, cb.BIND_MODE, ],
                        From=cb,
                        Where=cb.CALENDAR_RESOURCE_ID == calendar_rid,
                    ).on(sqlTxn)
                    calendars_for_id[calendar_rid] = ids

                if viewer:
                    calendarHome = (yield sqlTxn.calendarHomeWithUID(viewer))
                else:
                    calendarHome = None
                    for row in calendars_for_id[calendar_rid]:
                        home_id, bind_mode = row
                        if bind_mode == _BIND_MODE_OWN:
                            calendarHome = (yield sqlTxn.calendarHomeWithResourceID(home_id))
                            break

                if calendarHome is not None:
                    prop = WebDAVDocument.fromString(value).root_element
                    calendar = (yield calendarHome.childWithID(calendar_rid))
                    if calendar is not None:
                        yield calendar.setTimezone(prop.calendar())

            # Always delete the rows so that batch processing works correctly
            yield Delete(
                From=rp,
                Where=(rp.RESOURCE_ID.In(Parameter("ids", len(delete_ids)))).And
                      (rp.NAME == PropertyName.fromElement(caldavxml.CalendarTimeZone).toString()),
            ).on(sqlTxn, ids=delete_ids)

            yield sqlTxn.commit()

        yield cleanPropertyStore()

    except RuntimeError:
        f = Failure()
        yield sqlTxn.abort()
        f.raiseException()
Exemple #8
0
def doToEachHomeNotAtVersion(store, homeSchema, version, doIt, logStr, filterOwnerUID=None, processExternal=False):
    """
    Do something to each home whose version column indicates it is older
    than the specified version. Do this in batches as there may be a lot of work to do. Also,
    allow the GUID to be filtered to support a parallel mode of operation.
    """

    txn = store.newTransaction("updateDataVersion")
    where = homeSchema.DATAVERSION < version
    if filterOwnerUID:
        where = where.And(homeSchema.OWNER_UID.StartsWith(filterOwnerUID))
    total = (yield Select(
        [Count(homeSchema.RESOURCE_ID), ],
        From=homeSchema,
        Where=where,
    ).on(txn))[0][0]
    yield txn.commit()
    count = 0

    while True:

        logUpgradeStatus(logStr, count, total)

        # Get the next home with an old version
        txn = store.newTransaction("updateDataVersion")
        try:
            rows = yield Select(
                [homeSchema.RESOURCE_ID, homeSchema.OWNER_UID, homeSchema.STATUS, ],
                From=homeSchema,
                Where=where,
                OrderBy=homeSchema.OWNER_UID,
                Limit=1,
            ).on(txn)

            if len(rows) == 0:
                yield txn.commit()
                logUpgradeStatus("End {}".format(logStr), count, total)
                returnValue(None)

            # Apply to the home if not external
            homeResourceID, _ignore_owner_uid, homeStatus = rows[0]
            if homeStatus != _HOME_STATUS_EXTERNAL or processExternal:
                yield doIt(txn, homeResourceID)

            # Update the home to the current version
            yield Update(
                {homeSchema.DATAVERSION: version},
                Where=homeSchema.RESOURCE_ID == homeResourceID,
            ).on(txn)
            yield txn.commit()
        except RuntimeError, e:
            f = Failure()
            logUpgradeError(
                logStr,
                "Failed to upgrade {} to {}: {}".format(homeSchema, version, e)
            )
            yield txn.abort()
            f.raiseException()

        count += 1
Exemple #9
0
 def handle_errors(self, failure: Failure) -> None:
     cleaned_traceback = self.clean_traceback(failure)
     self.log.warn(
         f"Unhandled error during operator bonded check: {cleaned_traceback}"
     )
     if failure.check([self.OperatorNoLongerBonded]):
         # this type of exception we want to propagate because we will shut down
         failure.raiseException()
Exemple #10
0
    def http_POST(self, request):
        """
        The server-to-server POST method.
        """

        # Need a transaction to work with
        txn = transactionFromRequest(request, self._newStore)

        # Log extended item
        if not hasattr(request, "extendedLogItems"):
            request.extendedLogItems = {}

        # This is a server-to-server scheduling operation.
        scheduler = IScheduleScheduler(txn,
                                       None,
                                       logItems=request.extendedLogItems,
                                       podding=self._podding)

        # Check content first
        contentType = request.headers.getHeader("content-type")
        format = self.determineType(contentType)

        if format is None:
            msg = "MIME type {} not allowed in iSchedule request".format(
                contentType, )
            self.log.error(msg)
            raise HTTPError(
                scheduler.errorResponse(
                    responsecode.FORBIDDEN,
                    (ischedule_namespace, "invalid-calendar-data-type"),
                    msg,
                ))

        originator = self.loadOriginatorFromRequestHeaders(request)
        recipients = self.loadRecipientsFromRequestHeaders(request)
        body = (yield allDataFromStream(request.stream))
        calendar = Component.fromString(body, format=format)

        # Do the POST processing treating this as a non-local schedule
        try:
            result = (yield
                      scheduler.doSchedulingViaPOST(request.remoteAddr,
                                                    request.headers, body,
                                                    calendar, originator,
                                                    recipients))
        except Exception:
            ex = Failure()
            yield txn.abort()
            ex.raiseException()
        else:
            yield txn.commit()
        response = result.response(format=format)
        if not self._podding:
            response.headers.addRawHeader(
                ISCHEDULE_CAPABILITIES,
                str(config.Scheduling.iSchedule.SerialNumber))
        returnValue(response)
Exemple #11
0
    def getVersions(self):
        """
        Extract the expected version from the database schema and get the actual version in the current
        database, along with the DB dialect.
        """

        # Retrieve the version number from the schema file
        current_schema = self.schemaLocation.child("current.sql").getContent()
        found = re.search(
            "insert into CALENDARSERVER values \('%s', '(\d+)'\);" %
            (self.versionKey, ), current_schema)
        if found is None:
            msg = "Schema is missing required database key %s insert statement: %s" % (
                self.versionKey,
                current_schema,
            )
            self.log.error(msg)
            raise RuntimeError(msg)
        else:
            required_version = int(found.group(1))
            self.log.warn("Required database key %s: %s." % (
                self.versionKey,
                required_version,
            ))

        # Get the schema version in the current database
        sqlTxn = self.sqlStore.newTransaction(
            label="UpgradeDatabaseCoreStep.getVersions")
        dialect = sqlTxn.dialect
        try:
            actual_version = yield sqlTxn.calendarserverValue(self.versionKey)
            actual_version = int(actual_version)
            yield sqlTxn.commit()
        except (RuntimeError, ValueError):
            f = Failure()
            self.log.error("Database key %s cannot be determined." %
                           (self.versionKey, ))
            yield sqlTxn.abort()
            if self.defaultKeyValue is None:
                f.raiseException()
            else:
                actual_version = self.defaultKeyValue

        self.log.warn("Actual database key %s: %s." % (
            self.versionKey,
            actual_version,
        ))

        returnValue((
            dialect,
            required_version,
            actual_version,
        ))
def moveCalendarAvailabilityProperties(sqlStore):
    """
    Need to move all the CS:calendar-availability properties in the
    RESOURCE_PROPERTY table to the new CALENDAR_BIND table columns, extracting
    the new value from the XML property.
    """

    cb = schema.CALENDAR_BIND
    rp = schema.RESOURCE_PROPERTY

    try:
        while True:
            sqlTxn = sqlStore.newTransaction()
            rows = (yield rowsForProperty(sqlTxn, customxml.CalendarAvailability, batch=BATCH_SIZE))
            if len(rows) == 0:
                yield sqlTxn.commit()
                break

            # Map each calendar to a home id using a single query for efficiency
            calendar_ids = [row[0] for row in rows]

            home_map = yield Select(
                [cb.CALENDAR_RESOURCE_ID, cb.CALENDAR_HOME_RESOURCE_ID, ],
                From=cb,
                Where=(cb.CALENDAR_RESOURCE_ID.In(Parameter("ids", len(calendar_ids)))).And(cb.BIND_MODE == _BIND_MODE_OWN),
            ).on(sqlTxn, ids=calendar_ids)
            calendar_to_home = dict(home_map)

            # Move property to each home
            for calendar_rid, value in rows:
                if calendar_rid in calendar_to_home:
                    calendarHome = (yield sqlTxn.calendarHomeWithResourceID(calendar_to_home[calendar_rid]))

                    if calendarHome is not None:
                        prop = WebDAVDocument.fromString(value).root_element
                        yield calendarHome.setAvailability(prop.calendar())

            # Always delete the rows so that batch processing works correctly
            yield Delete(
                From=rp,
                Where=(rp.RESOURCE_ID.In(Parameter("ids", len(calendar_ids)))).And
                      (rp.NAME == PropertyName.fromElement(customxml.CalendarAvailability).toString()),
            ).on(sqlTxn, ids=calendar_ids)

            yield sqlTxn.commit()

        yield cleanPropertyStore()

    except RuntimeError:
        f = Failure()
        yield sqlTxn.abort()
        f.raiseException()
 def applyUpgrade(self, fp):
     """
     Apply the schema upgrade .sql file to the database.
     """
     self.log.warn("Applying schema upgrade: %s" % (fp.basename(),))
     sqlTxn = self.sqlStore.newTransaction()
     try:
         sql = fp.getContent()
         yield sqlTxn.execSQLBlock(sql)
         yield sqlTxn.commit()
     except RuntimeError:
         f = Failure()
         yield sqlTxn.abort()
         f.raiseException()
Exemple #14
0
 def applyUpgrade(self, fp):
     """
     Apply the schema upgrade .sql file to the database.
     """
     self.log.warn("Applying schema upgrade: %s" % (fp.basename(), ))
     sqlTxn = self.sqlStore.newTransaction(
         label="UpgradeDatabaseSchemaStep.applyUpgrade")
     try:
         sql = fp.getContent()
         yield sqlTxn.execSQLBlock(sql)
         yield sqlTxn.commit()
     except RuntimeError:
         f = Failure()
         yield sqlTxn.abort()
         f.raiseException()
Exemple #15
0
    def startService(self):
        """Start All AppManager Services"""
        if self.scanning.called:  #need to pre-populate values
            self.scanning = defer.maybeDeferred(self._first_scan)
        self.first_run = True
        self._task = task.LoopingCall(self.scan_app_instances)

        #plugins will be created and loaded when needed
        for shortname in config.APPLICATIONS.keys():
            manager = None
            try:
                applog = logWithContext(type=shortname, route=SERVICENAME)
                applog('Loading Application Plugin')
                applog('Creating Application Manager')
                manager = AppManager(shortname)
                manager.parentService = self
                #check and see if the model is bound
                if not AppManager(shortname).running:
                    applog('Starting Application Manager')
                    manager.start()
            except:
                failure = Failure()
                #bad plugin, not adaptable
                failures = (InvalidPlugin, TypeError)
                if failure.check(*failures) and manager:
                    log('plugin for %s is invalid' % (manager.name, ))
                    manager.action.__class__.delete(manager.action)
                    try:
                        pluginFactory.delete_plugin(manager.model)
                    except:
                        pass  #silence
                    AppManager.delete(manager)
                    if not config.EXCESSIVE_LOGGING:
                        continue  #avoid extra logging
                try:
                    failure.raiseException()
                except:
                    crashReport('ApplicationLoader', self)
        Service.startService(self)
        Event('instance-started').subscribe(self.reset_tracking)
        #wire allapps action into the server
        drone.builtins.update({
            'allapps': self.allapps_action,
            'applist': self.applist_action,
        })
        #delay scanning by some interval
        config.reactor.callLater(SERVICECONFIG.initial_delay,
                                 self._start_all_tasks)
Exemple #16
0
    def http_POST(self, request):
        """
        The server-to-server POST method.
        """

        # Need a transaction to work with
        txn = transactionFromRequest(request, self._newStore)

        # This is a server-to-server scheduling operation.
        scheduler = IScheduleScheduler(txn, None, podding=self._podding)

        # Check content first
        contentType = request.headers.getHeader("content-type")
        format = self.determineType(contentType)

        if format is None:
            msg = "MIME type {} not allowed in iSchedule request".format(contentType,)
            self.log.error(msg)
            raise HTTPError(scheduler.errorResponse(
                responsecode.FORBIDDEN,
                (ischedule_namespace, "invalid-calendar-data-type"),
                msg,
            ))

        originator = self.loadOriginatorFromRequestHeaders(request)
        recipients = self.loadRecipientsFromRequestHeaders(request)
        body = (yield allDataFromStream(request.stream))
        calendar = Component.fromString(body, format=format)

        # Do the POST processing treating this as a non-local schedule
        try:
            result = (yield scheduler.doSchedulingViaPOST(request.remoteAddr, request.headers, body, calendar, originator, recipients))
        except Exception:
            ex = Failure()
            yield txn.abort()
            ex.raiseException()
        else:
            yield txn.commit()
        response = result.response(format=format)
        if not self._podding:
            response.headers.addRawHeader(ISCHEDULE_CAPABILITIES, str(config.Scheduling.iSchedule.SerialNumber))
        returnValue(response)
Exemple #17
0
   def startService(self):
       """Start All AppManager Services"""
       if self.scanning.called: #need to pre-populate values
           self.scanning = defer.maybeDeferred(self._first_scan)
       self.first_run = True
       self._task = task.LoopingCall(self.scan_app_instances)

       #plugins will be created and loaded when needed
       for shortname in config.APPLICATIONS.keys():
           manager = None
           try:
               applog = logWithContext(type=shortname,route=SERVICENAME)
               applog('Loading Application Plugin')
               applog('Creating Application Manager')
               manager = AppManager(shortname)
               manager.parentService = self
               #check and see if the model is bound
               if not AppManager(shortname).running:
                   applog('Starting Application Manager')
                   manager.start()
           except:
               failure = Failure()
               #bad plugin, not adaptable
               failures = (InvalidPlugin, TypeError)
               if failure.check(*failures) and manager:
                   log('plugin for %s is invalid' % (manager.name,))
                   manager.action.__class__.delete(manager.action)
                   try: pluginFactory.delete_plugin(manager.model)
                   except: pass #silence
                   AppManager.delete(manager)
                   if not config.EXCESSIVE_LOGGING: continue #avoid extra logging
               try: failure.raiseException()
               except: crashReport('ApplicationLoader', self)
       Service.startService(self)
       Event('instance-started').subscribe(self.reset_tracking)
       #wire allapps action into the server
       drone.builtins.update({
           'allapps': self.allapps_action,
           'applist': self.applist_action,
       })
       #delay scanning by some interval
       config.reactor.callLater(SERVICECONFIG.initial_delay, self._start_all_tasks)
Exemple #18
0
 def applyUpgrade(self, fp):
     """
     Apply the schema upgrade .sql file to the database.
     """
     self.log.warn("Applying schema upgrade: {path}", path=fp.basename())
     sqlTxn = self.sqlStore.newTransaction(label="UpgradeDatabaseSchemaStep.applyUpgrade")
     try:
         sql = fp.getContent()
         yield sqlTxn.execSQLBlock(sql)
         yield sqlTxn.commit()
     except (RuntimeError, StandardError) as e:
         if hasattr(e, "stmt"):
             self.log.error("Apply upgrade failed for '{basename}' on statement: {stmt}\n{err}".format(
                 basename=fp.basename(),
                 stmt=e.stmt,
                 err=e,
             ))
         f = Failure()
         yield sqlTxn.abort()
         f.raiseException()
Exemple #19
0
 def migrateOneHome(self, fileTxn, homeType, fileHome):
     """
     Migrate an individual calendar or addressbook home.
     """
     migrateFunc, destFunc = homeTypeLookup.get(homeType)
     uid = normalizeUUIDOrNot(fileHome.uid())
     self.log.warn("Starting migration transaction {type} UID {uid}",
                   type=homeType,
                   uid=uid)
     sqlTxn = self.sqlStore.newTransaction(
         label="UpgradeToDatabaseStep.migrateOneHome")
     homeGetter = destFunc(sqlTxn)
     sqlHome = yield homeGetter(uid, create=False)
     if sqlHome is not None and not self.merge:
         self.log.warn("{ty[e} home {uid} already existed not migrating",
                       type=homeType,
                       uid=uid)
         yield sqlTxn.abort()
         returnValue(None)
     try:
         if sqlHome is None:
             try:
                 sqlHome = yield homeGetter(uid, create=True)
             except DirectoryRecordNotFoundError:
                 # The directory record does not exist; skip this home
                 self.log.warn(
                     "Skipping migration of {uid} because it's missing from the directory service",
                     uid=uid)
                 returnValue(None)
         yield migrateFunc(fileHome, sqlHome, merge=self.merge)
     except:
         f = Failure()
         yield sqlTxn.abort()
         f.raiseException()
     else:
         yield sqlTxn.commit()
         # Remove file home after migration. FIXME: instead, this should be a
         # public remove...HomeWithUID() API for de-provisioning.  (If we had
         # this, this would simply be a store-to-store migrator rather than a
         # filesystem-to-database upgrade.)
         fileHome._path.remove()
    def getVersions(self):
        """
        Extract the expected version from the database schema and get the actual version in the current
        database, along with the DB dialect.
        """

        # Retrieve the version number from the schema file
        current_schema = self.schemaLocation.child("current.sql").getContent()
        found = re.search("insert into CALENDARSERVER values \('%s', '(\d+)'\);" % (self.versionKey,), current_schema)
        if found is None:
            msg = "Schema is missing required database key %s insert statement: %s" % (self.versionKey, current_schema,)
            self.log.error(msg)
            raise RuntimeError(msg)
        else:
            required_version = int(found.group(1))
            self.log.warn("Required database key %s: %s." % (self.versionKey, required_version,))

        # Get the schema version in the current database
        sqlTxn = self.sqlStore.newTransaction()
        dialect = sqlTxn.dialect
        try:
            actual_version = yield sqlTxn.calendarserverValue(self.versionKey)
            actual_version = int(actual_version)
            yield sqlTxn.commit()
        except (RuntimeError, ValueError):
            f = Failure()
            self.log.error("Database key %s cannot be determined." % (self.versionKey,))
            yield sqlTxn.abort()
            if self.defaultKeyValue is None:
                f.raiseException()
            else:
                actual_version = self.defaultKeyValue

        self.log.warn("Actual database key %s: %s." % (self.versionKey, actual_version,))

        returnValue((dialect, required_version, actual_version,))
def _processDefaultCalendarProperty(sqlStore, propname, colname):
    """
    Move the specified property value to the matching CALENDAR_HOME_METADATA table column.

    Since the number of calendar homes may well be large, we need to do this in batches.
    """

    cb = schema.CALENDAR_BIND
    rp = schema.RESOURCE_PROPERTY

    try:
        while True:
            sqlTxn = sqlStore.newTransaction()
            rows = (yield rowsForProperty(sqlTxn, propname, batch=BATCH_SIZE))
            if len(rows) == 0:
                yield sqlTxn.commit()
                break
            delete_ids = []
            for inbox_rid, value in rows:
                delete_ids.append(inbox_rid)
                ids = yield Select(
                    [cb.CALENDAR_HOME_RESOURCE_ID, ],
                    From=cb,
                    Where=cb.CALENDAR_RESOURCE_ID == inbox_rid,
                ).on(sqlTxn)
                if len(ids) > 0:

                    calendarHome = (yield sqlTxn.calendarHomeWithResourceID(ids[0][0]))
                    if calendarHome is not None:

                        prop = WebDAVDocument.fromString(value).root_element
                        defaultCalendar = str(prop.children[0])
                        parts = defaultCalendar.split("/")
                        if len(parts) == 5:

                            calendarName = parts[-1]
                            calendarHomeUID = parts[-2]
                            expectedHome = (yield sqlTxn.calendarHomeWithUID(calendarHomeUID))
                            if expectedHome is not None and expectedHome.id() == calendarHome.id():

                                calendar = (yield calendarHome.calendarWithName(calendarName))
                                if calendar is not None:
                                    yield calendarHome.setDefaultCalendar(
                                        calendar, tasks=(propname == customxml.ScheduleDefaultTasksURL)
                                    )

            # Always delete the rows so that batch processing works correctly
            yield Delete(
                From=rp,
                Where=(rp.RESOURCE_ID.In(Parameter("ids", len(delete_ids)))).And
                      (rp.NAME == PropertyName.fromElement(propname).toString()),
            ).on(sqlTxn, ids=delete_ids)

            yield sqlTxn.commit()

        yield cleanPropertyStore()

    except RuntimeError:
        f = Failure()
        yield sqlTxn.abort()
        f.raiseException()
def _processDefaultAlarmProperty(sqlStore, propname, vevent, timed):
    """
    Move the specified property value to the matching CALENDAR_HOME_METADATA or CALENDAR_BIND table column.

    Since the number of properties may well be large, we need to do this in batches.
    """

    hm = schema.CALENDAR_HOME_METADATA
    cb = schema.CALENDAR_BIND
    rp = schema.RESOURCE_PROPERTY

    try:
        calendars_for_id = {}
        while True:
            sqlTxn = sqlStore.newTransaction()
            rows = (yield rowsForProperty(sqlTxn, propname, with_uid=True, batch=BATCH_SIZE))
            if len(rows) == 0:
                yield sqlTxn.commit()
                break
            delete_ids = []
            for rid, value, viewer in rows:
                delete_ids.append(rid)

                prop = WebDAVDocument.fromString(value).root_element
                alarm = str(prop.children[0]) if prop.children else None

                # First check if the rid is a home - this is the most common case
                ids = yield Select(
                    [hm.RESOURCE_ID, ],
                    From=hm,
                    Where=hm.RESOURCE_ID == rid,
                ).on(sqlTxn)

                if len(ids) > 0:
                    # Home object
                    calendarHome = (yield sqlTxn.calendarHomeWithResourceID(ids[0][0]))
                    if calendarHome is not None:
                        yield calendarHome.setDefaultAlarm(alarm, vevent, timed)
                else:
                    # rid is a calendar - we need to find the per-user calendar for the resource viewer
                    if rid not in calendars_for_id:
                        ids = yield Select(
                            [cb.CALENDAR_HOME_RESOURCE_ID, cb.BIND_MODE, ],
                            From=cb,
                            Where=cb.CALENDAR_RESOURCE_ID == rid,
                        ).on(sqlTxn)
                        calendars_for_id[rid] = ids

                    if viewer:
                        calendarHome = (yield sqlTxn.calendarHomeWithUID(viewer))
                    else:
                        calendarHome = None
                        for row in calendars_for_id[rid]:
                            home_id, bind_mode = row
                            if bind_mode == _BIND_MODE_OWN:
                                calendarHome = (yield sqlTxn.calendarHomeWithResourceID(home_id))
                                break

                    if calendarHome is not None:
                        calendar = yield calendarHome.childWithID(rid)
                        if calendar is not None:
                            yield calendar.setDefaultAlarm(alarm, vevent, timed)

            # Always delete the rows so that batch processing works correctly
            yield Delete(
                From=rp,
                Where=(rp.RESOURCE_ID.In(Parameter("ids", len(delete_ids)))).And
                      (rp.NAME == PropertyName.fromElement(propname).toString()),
            ).on(sqlTxn, ids=delete_ids)

            yield sqlTxn.commit()

        yield cleanPropertyStore()

    except RuntimeError:
        f = Failure()
        yield sqlTxn.abort()
        f.raiseException()