def _DisplayMain(self): query = models.AppleSUSProduct.AllActive().order('-apple_mtime') products = [] # NOTE(user): the following adds about 700ms onto the request, so we may # want to pre-calculate this in a cron in the future. for p in gae_util.QueryIterator(query, step=100): if common.STABLE not in p.tracks: p.stable_promote_date = applesus.GetAutoPromoteDate( common.STABLE, p) if common.TESTING not in p.tracks: p.testing_promote_date = applesus.GetAutoPromoteDate( common.TESTING, p) products.append(p) catalogs = [] for os_version in applesus.OS_VERSIONS: os_catalogs = {'os_version': os_version} for track in ['untouched'] + common.TRACKS: catalog_key = '%s_%s' % (os_version, track) c = models.AppleSUSCatalog.MemcacheWrappedGet(catalog_key) os_catalogs[track] = c.mtime if c else None catalogs.append(os_catalogs) catalogs_pending = {} for track in common.TRACKS: catalogs_pending[track] = False for os_version in applesus.OS_VERSIONS: lock_name = applesus.CatalogRegenerationLockName( track, os_version) catalogs_pending[track] |= gae_util.LockExists(lock_name) install_counts, counts_mtime = models.ReportsCache.GetInstallCounts() data = { 'catalogs': catalogs, 'catalogs_pending': catalogs_pending, 'products': products, 'install_counts': install_counts, 'install_counts_mtime': counts_mtime, 'tracks': common.TRACKS, 'auto_promote_enabled': settings.APPLE_AUTO_PROMOTE_ENABLED, 'auto_promote_stable_weekday': calendar.day_name[settings.APPLE_AUTO_PROMOTE_STABLE_WEEKDAY], 'unstable_grace_period_days': settings.APPLE_UNSTABLE_GRACE_PERIOD_DAYS, 'testing_grace_period_days': settings.APPLE_TESTING_GRACE_PERIOD_DAYS, 'report_type': 'apple_applesus' } self.Render('applesus_list.html', data)
def testGetAutoPromoteDateOverride(self): """Test GetAutoPromoteDate() for a product that has manual_override set.""" applesus_product = self.mox.CreateMockAnything() applesus_product.manual_override = True self.mox.ReplayAll() d = applesus.GetAutoPromoteDate(applesus.common.TESTING, applesus_product) self.assertEqual(d, None) self.mox.VerifyAll()
def testGetAutoPromoteDateNotInUnstable(self): """Test GetAutoPromoteDate() for a product that's not in unstable.""" applesus_product = self.mox.CreateMockAnything() applesus_product.manual_override = False applesus_product.tracks = [] self.mox.ReplayAll() d = applesus.GetAutoPromoteDate(applesus.common.TESTING, applesus_product) self.assertEqual(d, None) self.mox.VerifyAll()
def _DisplayMain(self): query = models.AppleSUSProduct.AllActive().order('-apple_mtime') products = [] # NOTE(user): the following adds about 700ms onto the request, so we may # want to pre-calculate this in a cron in the future. for p in query: if common.STABLE not in p.tracks: p.stable_promote_date = applesus.GetAutoPromoteDate( common.STABLE, p) if common.TESTING not in p.tracks: p.testing_promote_date = applesus.GetAutoPromoteDate( common.TESTING, p) products.append(p) catalogs = [] for os_version in applesus.OS_VERSIONS: c = models.AppleSUSCatalog.MemcacheWrappedGet('%s_untouched' % os_version) if c: catalogs.append({ 'version': os_version, 'download_datetime': c.mtime }) data = { 'catalogs': catalogs, 'products': products, 'tracks': common.TRACKS, 'auto_promote_enabled': settings.APPLE_AUTO_PROMOTE_ENABLED, 'auto_promote_stable_weekday': calendar.day_name[settings.APPLE_AUTO_PROMOTE_STABLE_WEEKDAY], 'unstable_grace_period_days': settings.APPLE_UNSTABLE_GRACE_PERIOD_DAYS, 'testing_grace_period_days': settings.APPLE_TESTING_GRACE_PERIOD_DAYS, 'report_type': 'apple_applesus' } self.Render('applesus_list.html', data)
def testGetAutoPromoteDateStableButNotYetInTestingButTestingDelayed(self): """Test GetAutoPromoteDate() for stable where product not yet in testing.""" applesus_product = self.mox.CreateMockAnything() applesus_product.mtime = datetime.datetime(2011, 7, 26, 00, 00, 00) applesus_product.manual_override = False applesus_product.tracks = [applesus.common.UNSTABLE] auto_promote_date = datetime.date(2011, 8, 10) self.mox.ReplayAll() d = applesus.GetAutoPromoteDate(applesus.common.STABLE, applesus_product) self.assertEqual(d, auto_promote_date) self.mox.VerifyAll()
def testGetAutoPromoteDateTesting(self): """Test GetAutoPromoteDate() for testing track.""" applesus_product = self.mox.CreateMockAnything() applesus_product.mtime = datetime.datetime(2011, 7, 22, 00, 00, 00) applesus_product.manual_override = False applesus_product.tracks = [applesus.common.UNSTABLE] auto_promote_date = datetime.date(2011, 7, 26) self.mox.ReplayAll() d = applesus.GetAutoPromoteDate(applesus.common.TESTING, applesus_product) self.assertEqual(d, auto_promote_date) self.mox.VerifyAll()
def testGetAutoPromoteDateTestingSaturday(self): """Test GetAutoPromoteDate() for testing track.""" applesus_product = self.mox.CreateMockAnything() # 2011-07-18 + AUTO_PROMOTE_PHASE_DAYS_MAP['testing'] is a Saturday. applesus_product.mtime = datetime.datetime(2011, 7, 19, 00, 00, 00) applesus_product.manual_override = False applesus_product.tracks = [applesus.common.UNSTABLE] # So don't promote on 2011-07-18 + AUTO_PROMOTE_PHASE_DAYS_MAP['testing'], # instead delay until the following Monday. auto_promote_date = datetime.date(2011, 7, 25) self.mox.ReplayAll() d = applesus.GetAutoPromoteDate(applesus.common.TESTING, applesus_product) self.assertEqual(d, auto_promote_date) self.mox.VerifyAll()
def _ReadyToAutoPromote(self, applesus_product, track): """Returns boolean whether AppleSUSProduct should be promoted or not. Args: applesus_product: models.AppleSUSProduct object. track: str track like testing or stable. Returns: Boolean. True if the product is ready to promote, False otherwise. """ today = datetime.datetime.utcnow().date() auto_promote_date = applesus.GetAutoPromoteDate( track, applesus_product) if auto_promote_date and auto_promote_date <= today: return True return False
def _ReadyToAutoPromote(self, applesus_product, track, now=None): """Returns boolean whether AppleSUSProduct should be promoted or not. Args: applesus_product: models.AppleSUSProduct object. track: str track like testing or stable. now: datetime.datetime, optional, supply an alternative value for the current date/time. Returns: Boolean. True if the product is ready to promote, False otherwise. """ now = now or datetime.datetime.utcnow() today = now.date() hour = now.strftime('%H') auto_promote_date = applesus.GetAutoPromoteDate( track, applesus_product) if auto_promote_date and auto_promote_date <= today: if settings.HOUR_START <= int(hour) <= settings.HOUR_STOP: return True return False
def _DisplayAppleSusPromoCalendar(self): """Display upcoming Apple SUS updates in iCal format.""" now = datetime.datetime.utcnow().date() query = models.AppleSUSProduct.all().order('-apple_mtime') dates = {} # NOTE(user): the following adds about 700ms onto the request, so we may # want to pre-calculate this in a cron in the future. for p in query: if p.manual_override: continue if not common.UNSTABLE in p.tracks: continue if common.STABLE not in p.tracks: p.stable_promote_date = applesus.GetAutoPromoteDate( common.STABLE, p) if common.TESTING not in p.tracks: p.testing_promote_date = applesus.GetAutoPromoteDate( common.TESTING, p) if hasattr(p, 'stable_promote_date') and p.stable_promote_date >= now: dates.setdefault(p.stable_promote_date, []).append(p) if hasattr( p, 'testing_promote_date') and p.testing_promote_date >= now: dates.setdefault(p.testing_promote_date, []).append(p) dtstamp = datetime.datetime.utcnow() cal = icalendar.Calendar() for d in dates: e = icalendar.Event() e.add('dtstamp', dtstamp) e.add('summary', 'Apple SUS auto-promote') e.add('dtstart', d) e.add('transp', 'TRANSPARENT') products = {common.TESTING: [], common.STABLE: []} for p in dates[d]: track = None if p.stable_promote_date == d: track = common.STABLE elif p.testing_promote_date == d: track = common.TESTING products[track].append(' %s %s (%s)' % (p.name, p.version, p.product_id)) desc = [] for track in [common.STABLE, common.TESTING]: if not products[track]: continue desc.append('Auto-promoting to %s:' % track.upper()) desc.append('\n'.join(products[track])) e.add('description', '\n\n'.join(desc)) e['uid'] = '%s-simian-applesus' % d.strftime('%Y%m%d') cal.add_component(e) self.response.headers['Content-Type'] = 'text/calendar' self.response.out.write(cal.as_string())
def _ChangeProduct(self, product_id): """Method to change properties of a given Apple SUS product.""" user = users.get_current_user() track = self.request.get('track') enabled = self.request.get('enabled', None) manual_override = self.request.get('manual_override', None) unattended = self.request.get('unattended', None) force_install_after_date = self.request.get('force_install_after_date', None) product = models.AppleSUSProduct.get_by_key_name(product_id) if not product: #logging.warning('POST to unknown applesus product_id: %s', product_id) self.response.set_status(404) return log_args = {} data = { 'product_id': product_id, } # set/unset manual_override property if manual_override is not None: manual_override = bool(int(manual_override)) product.manual_override = manual_override product.put() #logging.info( # 'Manual override on Apple SUS %s: %s', # product_id, manual_override) log_action = 'manual_override=%s' % manual_override for track in common.TRACKS: if track not in product.tracks: prom_date = applesus.GetAutoPromoteDate(track, product) if prom_date: data['%s_promote_date' % track] = prom_date.strftime('%b. %d, %Y') data['manual_override'] = manual_override # set/unset force_install_after_date property elif force_install_after_date is not None: if force_install_after_date: product.force_install_after_date_str = force_install_after_date else: product.force_install_after_date = None product.put() data['force_install_after_date'] = force_install_after_date log_action = 'force_install_after_date=%s' % force_install_after_date # set/unset unattended property elif unattended is not None: unattended = bool(int(unattended)) product.unattended = unattended product.put() data['unattended'] = unattended log_action = 'unattended=%s' % unattended # add/remove track to product elif enabled is not None: enabled = bool(int(enabled)) if enabled: if track not in product.tracks: #logging.info('Adding %s to Apple SUS %s catalog', product_id, track) product.tracks.append(track) product.put() else: if track in product.tracks: #logging.info( # 'Removing %s from Apple SUS %s catalog', product_id, track) product.tracks.remove(track) product.put() log_action = '%s=%s' % (track, enabled) data.update({'track': track, 'enabled': enabled}) log = models.AdminAppleSUSProductLog(product_id=product_id, action=log_action, tracks=product.tracks, user=user.email()) log.put() self.response.headers['Content-Type'] = 'application/json' self.response.out.write(json.dumps(data))
def _ChangeProduct(self, product_id): """Method to change properties of a given Apple SUS product.""" user = users.get_current_user() track = self.request.get('track') enabled = self.request.get('enabled', None) manual_override = self.request.get('manual_override', None) unattended = self.request.get('unattended', None) force_install_after_date = self.request.get('force_install_after_date', None) product = models.AppleSUSProduct.get_by_key_name(product_id) if not product: self.response.set_status(httplib.NOT_FOUND) return data = { 'product_id': product_id, } changed_tracks = set() # set/unset manual_override property if manual_override is not None: manual_override = bool(int(manual_override)) product.manual_override = manual_override product.put() log_action = 'manual_override=%s' % manual_override for track in common.TRACKS: if track not in product.tracks: prom_date = applesus.GetAutoPromoteDate(track, product) if prom_date: data['%s_promote_date' % track] = prom_date.strftime('%b. %d, %Y') data['manual_override'] = manual_override # set/unset force_install_after_date property elif force_install_after_date is not None: if force_install_after_date: try: tomorrow = datetime.datetime.utcnow() + datetime.timedelta( hours=12) if datetime.datetime.strptime( # only allow future force install date force_install_after_date, '%Y-%m-%d %H:%M') > tomorrow: product.force_install_after_date_str = force_install_after_date else: self.error(httplib.BAD_REQUEST) return except ValueError: self.error(httplib.BAD_REQUEST) return else: product.force_install_after_date = None product.put() data['force_install_after_date'] = force_install_after_date log_action = 'force_install_after_date=%s' % force_install_after_date changed_tracks.update(product.tracks) # set/unset unattended property elif unattended is not None: unattended = bool(int(unattended)) product.unattended = unattended product.put() data['unattended'] = unattended log_action = 'unattended=%s' % unattended changed_tracks.update(product.tracks) # add/remove track to product elif enabled is not None: enabled = bool(int(enabled)) if enabled: if track not in product.tracks: product.tracks.append(track) product.put() else: if track in product.tracks: product.tracks.remove(track) product.put() log_action = '%s=%s' % (track, enabled) data.update({'track': track, 'enabled': enabled}) changed_tracks.add(track) log = models.AdminAppleSUSProductLog(product_id=product_id, action=log_action, tracks=product.tracks, user=user.email()) log.put() # Send email notification to admins if mail and settings.EMAIL_ON_EVERY_CHANGE: display_name = '%s - %s' % (product.name, product.version) subject = 'Apple SUS Update by %s - %s (%s)' % (user, display_name, product_id) body = '%s has set \'%s\' on %s.\n' % (user, log_action, display_name) body += '%s is now in %s track(s).\n' % (product_id, ', '.join( map(str, product.tracks))) mail.SendMail(settings.EMAIL_ADMIN_LIST, subject, body) # Regenerate catalogs for any changed tracks, if a task isn't already # queued to do so. for track in changed_tracks: if gae_util.ObtainLock(applesus.CATALOG_REGENERATION_LOCK_NAME % track): deferred.defer(applesus.GenerateAppleSUSCatalogs, track=track, delay=180) # TODO(user): add a visual cue to UI so admins know a generation is pending. self.response.headers['Content-Type'] = 'application/json' self.response.out.write(json.dumps(data))