def _DisplayHost(self, uuid=None, computer=None): """Displays the report for a single host. Args: uuid: str uuid for host to display. computer: models.Computer object to display. """ if not computer: computer = models.Computer.get_by_key_name(uuid) else: uuid = computer.uuid client_log_files = models.ClientLogFile.all().filter( 'uuid =', uuid).order('-mtime').fetch(100) msu_log = models.ComputerMSULog.all().filter( 'uuid =', uuid).order('-mtime').fetch(100) applesus_installs = models.InstallLog.all().filter( 'uuid =', uuid).filter( 'applesus =', True).order('-mtime').fetch(SINGLE_HOST_DATA_FETCH_LIMIT) installs = models.InstallLog.all().filter('uuid =', uuid).filter( 'applesus =', False).order('-mtime').fetch(SINGLE_HOST_DATA_FETCH_LIMIT) exits = models.PreflightExitLog.all().filter( 'uuid =', uuid).order('-mtime').fetch(SINGLE_HOST_DATA_FETCH_LIMIT) install_problems = models.ClientLog.all().filter( 'action =', 'install_problem').filter( 'uuid =', uuid).order('-mtime').fetch(SINGLE_HOST_DATA_FETCH_LIMIT) uptime = None if computer: AddTimezoneToComputerDatetimes(computer) computer.connection_dates.reverse() computer.connection_datetimes.reverse() if computer.uptime: uptime_days = datetime.timedelta(seconds=computer.uptime).days uptime_hms = time.strftime('%H:%M:%S', time.gmtime(computer.uptime)) uptime = '%d days, %s' % (uptime_days, uptime_hms) else: uptime = 'unknown' values = { 'uuid_lookup_url': settings.UUID_LOOKUP_URL, 'owner_lookup_url': settings.OWNER_LOOKUP_URL, 'computer': computer, 'applesus_installs': applesus_installs, 'installs': installs, 'client_log_files': client_log_files, 'msu_log': msu_log, 'install_problems': install_problems, 'preflight_exits': exits, 'uptime': uptime, 'host_report': True, 'limit': SINGLE_HOST_DATA_FETCH_LIMIT, 'is_admin': auth.IsAdminUser(), 'is_support_user': auth.IsSupportUser(), 'is_security_user': auth.IsSecurityUser(), } self.response.out.write( RenderTemplate('templates/stats_host.html', values))
def get(self): """GET handler.""" can_mod_tags = (self.IsAdminUser() or auth.IsSupportUser or auth.IsSecurityUser()) tags = models.Tag.all() tags = sorted(tags, key=lambda t: unicode.lower(t.key().name())) d = {'tags': tags, 'can_mod_tags': can_mod_tags, 'report_type': 'tags'} self.Render('tags.html', d)
def post(self): """POST handler.""" can_mod_tags = (self.IsAdminUser() or auth.IsSupportUser or auth.IsSecurityUser()) if not can_mod_tags: return tag = self.request.get('tag').strip() tag = urllib.unquote(tag) action = self.request.get('action') if action == 'create': t = models.Tag(key_name=tag) uuid = self.request.get('uuid') if uuid: key = db.Key.from_path('Computer', uuid) t.keys.append(key) t.put() msg = 'Tag successfully saved.' elif action == 'delete': tag_manifest_mods = models.TagManifestModification.all().filter( 'tag_key_name =', tag).get() if tag_manifest_mods: msg = 'Tag not deleted as it\'s being used for Manifest Modifications.' else: t = models.Tag.get_by_key_name(tag) if t: t.delete() else: self.error(404) return msg = 'Tag successfully deleted.' elif action == 'change': uuid = self.request.get('uuid') add_tag = self.request.get('add') == '1' t = models.Tag.get_by_key_name(tag) if not t: self.error(404) return key = db.Key.from_path('Computer', uuid) if add_tag: t.keys.append(key) else: if key in t.keys: t.keys.remove(key) t.put() msg = 'Tag successfully modified' self.redirect('/admin/tags?msg=%s' % msg)
def _DisplayMain(self): """Displays the main Manifest Modification report.""" error = self.request.get('error') mod_type = self.request.get('mod_type') or 'owner' model = models.MANIFEST_MOD_MODELS.get(mod_type) if mod_type and not model: raise ValueError('Invalid mod_type: %s' % mod_type) elif mod_type: mods_query = model.all().order('-mtime') mods = self.Paginate(mods_query, DEFAULT_MANIFEST_MOD_FETCH_LIMIT) is_admin = self.IsAdminUser() is_support = False is_security = False if not is_admin: is_support = auth.IsSupportUser() if not is_support: is_security = auth.IsSecurityUser() if is_admin: munki_pkg_names = models.PackageInfo.GetManifestModPkgNames( common.MANIFEST_MOD_ADMIN_GROUP) mod_types = MOD_TYPES elif is_support: munki_pkg_names = models.PackageInfo.GetManifestModPkgNames( common.MANIFEST_MOD_SUPPORT_GROUP) mod_types = MOD_GROUP_TYPES[common.MANIFEST_MOD_SUPPORT_GROUP] elif is_security: munki_pkg_names = models.PackageInfo.GetManifestModPkgNames( common.MANIFEST_MOD_SECURITY_GROUP) mod_types = MOD_GROUP_TYPES[common.MANIFEST_MOD_SECURITY_GROUP] else: munki_pkg_names = None mod_types = [] data = { 'mod_types': mod_types, 'mod_type': mod_type, 'mods': mods, 'error': error, 'can_add_manifest_mods': is_admin or is_support or is_security, 'munki_pkg_names': munki_pkg_names, 'install_types': common.INSTALL_TYPES, 'manifests': common.TRACKS, 'report_type': 'manifests_admin', } self.Render('manifest_modifications.html', data)
def post(self): """POST handler.""" if self.request.get('add_manifest_mod'): if (not self.IsAdminUser() and not auth.IsSupportUser() and not auth.IsSecurityUser()): self.response.set_status(httplib.FORBIDDEN) return self._AddManifestModification() elif self.IsAdminUser() and self.request.get('delete'): self._DeleteManifestModification() elif self.IsAdminUser() and self.request.get('enabled'): self._ToggleManifestModification() else: if not self.IsAdminUser(): self.response.set_status(httplib.FORBIDDEN) return self.response.set_status(httplib.NOT_FOUND)
def _DisplayBrokenClients(self): """Displays a report of broken clients.""" # client with broken python py_computers = models.ComputerClientBroken.all().filter( 'fixed =', False) py_computers = list(py_computers) for computer in py_computers: computer.details = computer.details.replace("'", "\\'") computer.details = computer.details.replace('"', "\\'") computer.details = re.sub('\n', '<br/>', computer.details) computer.broken_datetimes.reverse() # clients with zero connection zero_conn_computers = models.Computer.AllActive().filter( 'connections_on_corp =', 0).filter('connections_off_corp =', 0).fetch(COMPUTER_FETCH_LIMIT) zero_conn_computers = list(zero_conn_computers) zero_conn_computers.sort(key=lambda x: x.preflight_datetime, reverse=True) # clients with no recent postflight, but recent preflight # NOTE: this takes ~5s to complete in ~20k fleet where ~1400 clients have # old postflight_datetime. if far more clients are in this state then # then the query could cause DeadlineExceededError. pf_computers = [] now = datetime.datetime.utcnow() not_recent = now - datetime.timedelta(days=15) q = models.Computer.AllActive().filter('postflight_datetime <', not_recent) for c in q: if not c.preflight_datetime or not c.postflight_datetime: continue # already covered zero connection clients above. if (c.preflight_datetime - c.postflight_datetime).days > 7: pf_computers.append(c) pf_computers.sort(key=lambda x: x.preflight_datetime, reverse=True) self.response.out.write( RenderTemplate( 'templates/stats_brokenclients.html', { 'py_computers': py_computers, 'zero_conn_computers': zero_conn_computers, 'pf_computers': pf_computers, 'is_admin': auth.IsAdminUser(), 'is_security_user': auth.IsSecurityUser(), }))
def testDoUserAuthWithAllDomainUsersOff(self): self.stubs.Set(auth.settings, 'ALLOW_ALL_DOMAIN_USERS_READ_ACCESS', False) self.stubs.Set(auth, 'users', self.mox.CreateMock(auth.users)) self.mox.StubOutWithMock(auth, 'IsAdminUser') self.mox.StubOutWithMock(auth, 'IsSupportUser') self.mox.StubOutWithMock(auth, 'IsSecurityUser') self.mox.StubOutWithMock(auth, 'IsPhysicalSecurityUser') mock_user = self.mox.CreateMockAnything() email = '*****@*****.**' auth.users.get_current_user().AndReturn(mock_user) mock_user.email().AndReturn(email) auth.IsAdminUser(email).AndReturn(False) auth.IsSupportUser(email).AndReturn(False) auth.IsSecurityUser(email).AndReturn(False) auth.IsPhysicalSecurityUser(email).AndReturn(True) self.mox.ReplayAll() self.assertEqual(mock_user, auth.DoUserAuth()) self.mox.VerifyAll()
def _AddManifestModification(self): """Adds a new manifest modification to Datastore.""" mod_type = self.request.get('mod_type') targets = [ x.strip() for x in self.request.get('target').split(',') if x.strip() ] munki_pkg_name = self.request.get('munki_pkg_name').strip() manifests = self.request.get_all('manifests') install_types = self.request.get_all('install_types') remove_from_manifest = bool(self.request.get('remove-from-manifest')) # Security users are only able to inject specific packages. if not self.IsAdminUser(): grp = None if auth.IsSupportUser(): grp = common.MANIFEST_MOD_SUPPORT_GROUP # Support users can only inject items into optional_installs. install_types = ['optional_installs'] elif auth.IsSecurityUser(): grp = common.MANIFEST_MOD_SECURITY_GROUP # Security users can only inject items into managed_installs. install_types = ['managed_installs'] munki_pkg_names = models.PackageInfo.GetManifestModPkgNames( grp, only_names=True) if munki_pkg_name not in munki_pkg_names: self.response.out.write('You are not allowed to inject: %s' % munki_pkg_name) self.response.set_status(httplib.FORBIDDEN) return elif mod_type not in [k for k, _ in MOD_GROUP_TYPES.get(grp, [])]: self.response.out.write( 'You are not allowed to inject to: %s' % mod_type) self.response.set_status(httplib.FORBIDDEN) return # Validation. error_msg = None if not targets or not munki_pkg_name or not install_types: error_msg = ( 'target, munki_pkg_name, and install_types are all required') if not error_msg: for manifest in manifests: if manifest not in common.TRACKS: error_msg = 'manifest %s is not in %s' % (manifest, common.TRACKS) if not error_msg: for install_type in install_types: if install_type not in common.INSTALL_TYPES: error_msg = 'install_type %s is not in %s' % ( install_type, common.INSTALL_TYPES) if not error_msg: if not models.PackageInfo.all().filter('name =', munki_pkg_name).get(): error_msg = 'No package found with Munki name: %s' % munki_pkg_name if not error_msg and len(targets) > MAX_TARGETS_PER_POST: error_msg = 'too many targets' if error_msg: self.redirect('/admin/manifest_modifications?msg=%s' % error_msg) return to_put = [] for target in targets: mod = models.BaseManifestModification.GenerateInstance( mod_type, target, munki_pkg_name, manifests=manifests, install_types=install_types, user=users.get_current_user(), remove=remove_from_manifest) to_put.append(mod) gae_util.BatchDatastoreOp(db.put, to_put) for target in targets: models.BaseManifestModification.ResetModMemcache(mod_type, target) msg = 'Manifest Modification successfully saved.' self.redirect('/admin/manifest_modifications?mod_type=%s&msg=%s' % (mod_type, msg))
def _DisplayMain(self): """Displays the main Manifest Modification report.""" error_msg = self.request.get('error') mod_type = self.request.get('mod_type') or 'owner' model = models.MANIFEST_MOD_MODELS.get(mod_type) if mod_type and not model: error_msg = 'Unknown mod_type provided; defaulting to owner' mod_type = 'owner' model = models.MANIFEST_MOD_MODELS.get(mod_type) mods_query = model.all().order('-mtime') filter_value = self.request.get('filter_value') filter_field = self.request.get('filter_field') if filter_value: if filter_field == 'target': mods_query.filter(model.TARGET_PROPERTY_NAME, filter_value) elif filter_field == 'package': mods_query.filter('value', filter_value) elif filter_field == 'admin': if '@' not in filter_value: filter_value += '@' + settings.AUTH_DOMAIN mods_query.filter('user', users.User(email=filter_value)) mods = self.Paginate(mods_query, DEFAULT_MANIFEST_MOD_FETCH_LIMIT) is_admin = self.IsAdminUser() is_support = False is_security = False if not is_admin: is_support = auth.IsSupportUser() if not is_support: is_security = auth.IsSecurityUser() if is_admin: munki_pkg_names = models.PackageInfo.GetManifestModPkgNames( common.MANIFEST_MOD_ADMIN_GROUP) mod_types = MOD_TYPES elif is_support: munki_pkg_names = models.PackageInfo.GetManifestModPkgNames( common.MANIFEST_MOD_SUPPORT_GROUP) mod_types = MOD_GROUP_TYPES[common.MANIFEST_MOD_SUPPORT_GROUP] elif is_security: munki_pkg_names = models.PackageInfo.GetManifestModPkgNames( common.MANIFEST_MOD_SECURITY_GROUP) mod_types = MOD_GROUP_TYPES[common.MANIFEST_MOD_SECURITY_GROUP] else: munki_pkg_names = None mod_types = [] data = { 'mod_types': mod_types, 'mod_type': mod_type, 'mods': mods, 'error': error_msg, 'can_add_manifest_mods': is_admin or is_support or is_security, 'munki_pkg_names': munki_pkg_names, 'install_types': common.INSTALL_TYPES, 'manifests': common.TRACKS, 'report_type': 'manifests_admin', 'mods_filter': filter_field, 'mods_filter_value': filter_value, } self.Render('manifest_modifications.html', data)
def _AddManifestModification(self): """Adds a new manifest modification to Datastore.""" mod_type = self.request.get('mod_type') target = self.request.get('target').strip() munki_pkg_name = self.request.get('munki_pkg_name').strip() manifests = self.request.get_all('manifests') install_types = self.request.get_all('install_types') # Security users are only able to inject specific packages. if not self.IsAdminUser(): grp = None if auth.IsSupportUser(): grp = common.MANIFEST_MOD_SUPPORT_GROUP # Support users can only inject items into optional_installs. install_types = ['optional_installs'] elif auth.IsSecurityUser(): grp = common.MANIFEST_MOD_SECURITY_GROUP # Security users can only inject items into managed_installs. install_types = ['managed_installs'] munki_pkg_names = models.PackageInfo.GetManifestModPkgNames( grp, only_names=True) if munki_pkg_name not in munki_pkg_names: self.response.out.write('You are not allowed to inject: %s' % munki_pkg_name) self.response.set_status(403) return elif mod_type not in [k for k, v in MOD_GROUP_TYPES.get(grp, [])]: self.response.out.write( 'You are not allowed to inject to: %s' % mod_type) self.response.set_status(403) return # Validation. error_msg = None if not target or not munki_pkg_name or not install_types: error_msg = ( 'target, munki_pkg_name, and install_types are all required') if not error_msg: for manifest in manifests: if manifest not in common.TRACKS: error_msg = 'manifest %s is not in %s' % (manifest, common.TRACKS) if not error_msg: for install_type in install_types: if install_type not in common.INSTALL_TYPES: error_msg = 'install_type %s is not in %s' % ( install_type, common.INSTALL_TYPES) if not error_msg: if not models.PackageInfo.all().filter('name =', munki_pkg_name).get(): error_msg = 'No package found with Munki name: %s' % munki_pkg_name if error_msg: self.redirect('/admin/manifest_modifications?msg=%s' % error_msg) return mod = models.BaseManifestModification.GenerateInstance( mod_type, target, munki_pkg_name, manifests=manifests, install_types=install_types, user=users.get_current_user()) mod.put() models.BaseManifestModification.ResetModMemcache(mod_type, target) msg = 'Manifest Modification successfully saved.' self.redirect('/admin/manifest_modifications?mod_type=%s&msg=%s' % (mod_type, msg))
def _DisplayHost(self, uuid=None, computer=None): """Displays the report for a single host. Args: uuid: str uuid for host to display. computer: models.Computer object to display. """ if not uuid and not computer: self.response.set_status(404) return elif not computer: computer = models.Computer.get_by_key_name(uuid) else: uuid = computer.uuid popup = self.request.get('format', None) == 'popup' if popup: limit = 1 else: limit = SINGLE_HOST_DATA_FETCH_LIMIT client_log_files = models.ClientLogFile.all().filter( 'uuid =', uuid).order('-mtime').fetch(limit) msu_log = models.ComputerMSULog.all().filter( 'uuid =', uuid).order('-mtime').fetch(limit) applesus_installs = models.InstallLog.all().filter( 'uuid =', uuid).filter('applesus =', True).order('-mtime').fetch(limit) installs = models.InstallLog.all().filter('uuid =', uuid).filter( 'applesus =', False).order('-mtime').fetch(limit) exits = models.PreflightExitLog.all().filter( 'uuid =', uuid).order('-mtime').fetch(limit) install_problems = models.ClientLog.all().filter( 'action =', 'install_problem').filter('uuid =', uuid).order('-mtime').fetch(limit) tags = {} tags_list = [] if computer: # Generate tags data. tags_list = models.Tag.GetAllTagNamesForEntity(computer) for tag in tags_list: tags[tag] = True for tag in models.Tag.GetAllTagNames(): if tag not in tags: tags[tag] = False tags = json.dumps(tags, sort_keys=True) admin.AddTimezoneToComputerDatetimes(computer) computer.connection_dates.reverse() computer.connection_datetimes.reverse() try: uuid_lookup_url = settings.UUID_LOOKUP_URL except AttributeError: uuid_lookup_url = None try: owner_lookup_url = settings.OWNER_LOOKUP_URL except AttributeError: owner_lookup_url = None values = { 'uuid_lookup_url': uuid_lookup_url, 'owner_lookup_url': owner_lookup_url, 'computer': computer, 'applesus_installs': applesus_installs, 'installs': installs, 'client_log_files': client_log_files, 'msu_log': msu_log, 'install_problems': install_problems, 'preflight_exits': exits, 'tags': tags, 'tags_list': tags_list, 'host_report': True, 'limit': SINGLE_HOST_DATA_FETCH_LIMIT, 'is_support_user': auth.IsSupportUser(), 'is_security_user': auth.IsSecurityUser(), 'is_physical_security_user': auth.IsPhysicalSecurityUser(), } if popup: self.Render('host_popup.html', values) else: self.Render('host.html', values)
def _DisplayBrokenClients(self): """Displays a report of broken clients.""" # client with broken python py_computers = models.ComputerClientBroken.all().filter( 'fixed =', False) py_computers = list(py_computers) for computer in py_computers: computer.details = computer.details.replace("'", "\\'") computer.details = computer.details.replace('"', "\\'") computer.details = re.sub('\n', '<br/>', computer.details) computer.broken_datetimes.reverse() computer.likely_fixed = False # if a UUID is set, attempt to figure out when it last connected. if computer.uuid: try: c_obj = models.Computer.get_by_key_name(computer.uuid) if c_obj.preflight_datetime > computer.broken_datetimes[0]: computer.likely_fixed = True except (IndexError, TypeError, models.db.Error): pass # clients with zero connection q = models.Computer.AllActive().filter( 'connections_on_corp =', 0).filter('connections_off_corp =', 0).fetch(admin.DEFAULT_COMPUTER_FETCH_LIMIT) zero_conn_computers = [] for c in q: if c.preflight_count_since_postflight > PREFLIGHT_COUNT_BROKEN_THRESHOLD: zero_conn_computers.append(c) zero_conn_computers.sort(key=lambda x: x.preflight_datetime, reverse=True) # clients with no recent postflight, but recent preflight fetch_limit = 1000 pf_computers = [] q = models.Computer.AllActive().filter( 'preflight_count_since_postflight >', PREFLIGHT_COUNT_BROKEN_THRESHOLD) i = 0 for c in q: i += 1 if i >= fetch_limit: # avoid DeadlineExceededError. break if not c.preflight_datetime or not c.postflight_datetime: continue # already covered zero connection clients above. pf_computers.append(c) pf_computers.sort(key=lambda x: x.preflight_datetime, reverse=True) self.Render( 'broken_clients.html', { 'py_computers': py_computers, 'zero_conn_computers': zero_conn_computers, 'pf_computers': pf_computers, 'is_security_user': auth.IsSecurityUser(), 'report_type': 'broken_clients', 'truncated': i >= fetch_limit, 'preflight_count_broken_threshold': PREFLIGHT_COUNT_BROKEN_THRESHOLD, })
def _DisplayHost(self, computer, self_report): """Displays the report for a single host. Args: computer: models.Computer object to display. self_report: if True, display as self report. """ uuid = computer.uuid popup = self.request.get('format', None) == 'popup' if popup: limit = 1 else: limit = SINGLE_HOST_DATA_FETCH_LIMIT client_log_files = models.ClientLogFile.all().filter( 'uuid =', uuid).order('-mtime').fetch(limit) msu_log = models.ComputerMSULog.all().filter( 'uuid =', uuid).order('-mtime').fetch(limit) applesus_installs = models.InstallLog.all().filter( 'uuid =', uuid).filter('applesus =', True).order('-mtime').fetch(limit) installs = models.InstallLog.all().filter('uuid =', uuid).filter( 'applesus =', False).order('-mtime').fetch(limit) exits = models.PreflightExitLog.all().filter( 'uuid =', uuid).order('-mtime').fetch(limit) install_problems = models.ClientLog.all().filter( 'action =', 'install_problem').filter('uuid =', uuid).order('-mtime').fetch(limit) tags = {} tags_list = [] groups = {} groups_list = [] duplicates = [] if computer: # Generate tags data. tags_list = models.Tag.GetAllTagNamesForEntity(computer) for tag in tags_list: tags[tag] = True for tag in models.Tag.GetAllTagNames(): if tag not in tags: tags[tag] = False tags = json.dumps(tags, sort_keys=True) # Generate groups data. groups_list = models.Group.GetAllGroupNamesForUser(computer.owner) for group in groups_list: groups[group] = True for group in models.Group.GetAllGroupNames(): if group not in groups: groups[group] = False groups = json.dumps(groups, sort_keys=True) admin.AddTimezoneToComputerDatetimes(computer) computer.connection_dates.reverse() computer.connection_datetimes.reverse() duplicates = models.Computer.all().filter( 'serial =', computer.serial).fetch(20) duplicates = [e for e in duplicates if e.uuid != computer.uuid] try: uuid_lookup_url = settings.UUID_LOOKUP_URL except AttributeError: uuid_lookup_url = None try: owner_lookup_url = settings.OWNER_LOOKUP_URL except AttributeError: owner_lookup_url = None values = { 'report_type': 'host', 'uuid_lookup_url': uuid_lookup_url, 'owner_lookup_url': owner_lookup_url, 'client_site_enabled': settings.CLIENT_SITE_ENABLED, 'computer': computer, 'applesus_installs': applesus_installs, 'installs': installs, 'client_log_files': client_log_files, 'msu_log': msu_log, 'install_problems': install_problems, 'preflight_exits': exits, 'tags': tags, 'tags_list': tags_list, 'groups': groups, 'groups_list': groups_list, 'host_report': True, 'limit': SINGLE_HOST_DATA_FETCH_LIMIT, 'is_support_user': auth.IsSupportUser(), 'is_security_user': auth.IsSecurityUser(), 'is_physical_security_user': auth.IsPhysicalSecurityUser(), 'self_report': self_report, 'duplicates': duplicates, 'tags_xsrf_token': xsrf.XsrfTokenGenerate('tags'), 'groups_xsrf_token': xsrf.XsrfTokenGenerate('groups'), } if popup: self.Render('host_popup.html', values) else: self.Render('host.html', values)