def occurDay(self, start, now, skip=1, day=6, occur=0): startDateTime = datetime.fromtimestamp(start, self.tzInstance) timeDelta = relativedelta(hours=startDateTime.hour, minutes=startDateTime.minute, seconds=startDateTime.second) log.debug('start date: %s; day: %d; occur: %d; skip: %d', str(startDateTime), day, occur, skip) # get a list of (mday, wday) tuples for current month c = calendar.Calendar(firstweekday=0) flatter = sum( c.monthdays2calendar(startDateTime.year, startDateTime.month), []) if occur == 5: flatter = reversed(flatter) tmp_occur = 0 else: tmp_occur = occur count = 0 #find Nth occurrence of week day for mday, wday in flatter: if wday == day and mday > 0: count += 1 log.debug('found wday %d, mday %d, count %d', wday, mday, count) if count == tmp_occur + 1 and mday >= startDateTime.day: log.debug('count matched, mday %d', mday) startDateTime = datetime( startDateTime.year, startDateTime.month, mday, tzinfo=self.tzInstance) + timeDelta startTimestamp = Time.awareDatetimeToTimestamp( startDateTime) # do we need to skip this day? if skip > 1: log.debug('skipping this occurrence. skip = %d', skip) return self.occurDay(startTimestamp + DAY_SECONDS, now, skip - 1, day, tmp_occur) elif startTimestamp >= now: log.debug( 'Window will start on: %s', str( datetime.fromtimestamp(startTimestamp, self.tzInstance))) return startTimestamp # couldn't find start day in current month, switching to 1st day of the next month if startDateTime.month == 12: startDateTime = datetime(startDateTime.year + 1, 1, 1, tzinfo=self.tzInstance) else: startDateTime = datetime(startDateTime.year, startDateTime.month + 1, 1, tzinfo=self.tzInstance) startDateTime += timeDelta return self.occurDay(Time.awareDatetimeToTimestamp(startDateTime), now, skip, day, occur)
def getLocalizedTimestamp(year, month, day, hour, minutes): localizedExpectedDateTime = datetime(year, month, day, hour, minutes, tzinfo=tzInstance) return Time.awareDatetimeToTimestamp(localizedExpectedDateTime)
def addMonth(secs, dayOfMonthHint=0, tzInstance=tz.tzutc()): dateTime = datetime.fromtimestamp(secs, tzInstance) newYear = dateTime.year newMonth = dateTime.month + 1 if newMonth > 12: newYear += 1 newMonth = 1 lastDayOfMonth = calendar.monthrange(newYear, newMonth)[1] newDay = min(dayOfMonthHint, lastDayOfMonth) newDateTime = datetime(year=newYear, month=newMonth, day=newDay, hour=dateTime.hour, minute=dateTime.minute, second=dateTime.second, tzinfo=tzInstance) return Time.awareDatetimeToTimestamp(newDateTime)
def getLocalizedTimestamp(dateTime): localized_expected_time = dateTime.replace(tzinfo=tzInstance) return Time.awareDatetimeToTimestamp(localized_expected_time)
def _next(self, now): if not self.enabled: return None if self.skip is None: self.skip = 1 if now is None: now = time.time() if now < self.start: return self.start if self.repeat == self.NEVER: if now > self.start: return None return self.start elif self.repeat == self.DAILY: daysSince = (now - self.start) // DAY_SECONDS dateTime = datetime.fromtimestamp( self.start, self.tzInstance) + relativedelta(days=daysSince + self.skip) return Time.awareDatetimeToTimestamp(dateTime) elif self.repeat == self.EVERY_WEEKDAY: weeksSince = (now - self.start) // WEEK_SECONDS weekdaysSince = weeksSince * 5 # start at the most recent week-even point from the start baseDateTime = datetime.fromtimestamp( self.start, self.tzInstance) + relativedelta(weeks=weeksSince) nowDateTime = datetime.fromtimestamp(now, self.tzInstance) while 1: dow = baseDateTime.weekday() if dow not in (5, 6): if baseDateTime > nowDateTime and weekdaysSince % self.skip == 0: break weekdaysSince += 1 baseDateTime += relativedelta(days=1) assert baseDateTime >= nowDateTime return Time.awareDatetimeToTimestamp(baseDateTime) elif self.repeat == self.WEEKLY: weeksSince = (now - self.start) // WEEK_SECONDS dateTime = datetime.fromtimestamp( self.start, self.tzInstance) + relativedelta(weeks=weeksSince + self.skip) return Time.awareDatetimeToTimestamp(dateTime) elif self.repeat == self.MONTHLY: months = 0 m = self.start dayOfMonthHint = datetime.fromtimestamp(self.start, self.tzInstance).day while m < now or months % self.skip: m = addMonth(m, dayOfMonthHint, self.tzInstance) months += 1 return m elif self.repeat == self.NTHWDAY: return self.occurDay(self.start, now, self.skip, self.DAYS.index(self.days), self.OCCURRENCE.index(self.occurrence)) raise ValueError('bad value for MaintenanceWindow repeat: %r' % self.repeat)
def manage_editMaintenanceWindow( self, startDate='', startHours='00', startMinutes='00', durationDays='0', durationHours='00', durationMinutes='00', repeat='Never', days='Sunday', occurrence='1st', startProductionState=300, stopProductionState=RETURN_TO_ORIG_PROD_STATE, enabled=True, skip=1, REQUEST=None, startDateTime=None, timezone=None): "Update the maintenance window from GUI elements" def makeInt(v, fieldName, minv=None, maxv=None, acceptBlanks=True): if acceptBlanks: if isinstance(v, str): v = v.strip() v = v or '0' try: v = int(v) if minv is not None and v < minv: raise ValueError if maxv is not None and v > maxv: raise ValueError except ValueError: if minv is None and maxv is None: msg = '%s must be an integer.' % fieldName elif minv is not None and maxv is not None: msg = '%s must be between %s and %s inclusive.' % ( fieldName, minv, maxv) elif minv is not None: msg = '%s must be at least %s' % (fieldName, minv) else: msg = '%s must be no greater than %s' % (fieldName, maxv) msgs.append(msg) v = None return v oldAuditData = self.getAuditData() prodStates = dict( (key, value) for (key, value) in self.dmd.getProdStateConversions()) msgs = [] self.enabled = bool(enabled) if not timezone: # Use container timezone timezone = time.strftime('%Z') try: tzInstance = tz.gettz(timezone) except: msgs.append("'timezone' has wrong value") if startDateTime: t = int(startDateTime) else: startHours = int(startHours) if startHours else 0 startMinutes = int(startMinutes) if startMinutes else 0 self.enabled = bool(enabled) try: month, day, year = re.split('[^ 0-9]', startDate) except ValueError: msgs.append("Date needs three number fields") day = int(day) month = int(month) year = int(year) if not msgs: startDateTime = datetime(year, month, day, startHours, startMinutes, tzinfo=tzInstance) t = Time.awareDatetimeToTimestamp(startDateTime) if repeat not in self.REPEAT: msgs.append('\'repeat\' has wrong value.') if not isinstance(enabled, bool): msgs.append('\'enabled\' has wrong value, use true or false.') if not (startProductionState in prodStates.values() or prodStates.get(startProductionState, None)): msgs.append('\'startProductionState\' has wrong value.') elif isinstance(startProductionState, str): startProductionState = prodStates[startProductionState] if not msgs: durationDays = makeInt(durationDays, 'Duration days', minv=0) durationHours = makeInt(durationHours, 'Duration hours', minv=0, maxv=23) durationMinutes = makeInt(durationMinutes, 'Duration minutes', minv=0, maxv=59) if not msgs: duration = (durationDays * (60 * 24) + durationHours * 60 + durationMinutes) if duration < 1: msgs.append('Duration must be at least 1 minute.') if msgs: if REQUEST: messaging.IMessageSender(self).sendToBrowser( 'Window Edit Failed', '\n'.join(msgs), messaging.WARNING) else: raise Exception('Window Edit Failed: ' + '\n'.join(msgs)) else: self.start = t self.duration = duration self.repeat = repeat self.days = days self.occurrence = occurrence self.startProductionState = startProductionState self.stopProductionState = stopProductionState self.skip = skip self.timezone = timezone self.tzInstance = tzInstance now = time.time() if self.started: if ((t + duration * 60) < now) or (t > now) or (not self.enabled): # We're running. If we should have already ended OR the start was # moved into the future OR the MW is now disabled, end(). self.end() elif (t < now) and ((t + duration * 60) > now) and (self.enabled): # We aren't running, but we've scheduled the MW to be going on right now. self.begin() if REQUEST: flare = 'Maintenance window changes were saved.' if self.enabled: flare += ' Next run on %s' % time.strftime( "%m/%d/%Y %H:%M:%S", time.localtime(self.next())) messaging.IMessageSender(self).sendToBrowser( 'Window Updated', flare) audit('UI.MaintenanceWindow.Edit', self, data_=self.getAuditData(), oldData_=oldAuditData) if REQUEST: return REQUEST.RESPONSE.redirect(self.getUrlForUserCommands())