def testIsPanicModeNoPackages(self): """Test IsPanicModeNoPackages().""" self.mox.StubOutWithMock(common, 'IsPanicMode') common.IsPanicMode(common.PANIC_MODE_NO_PACKAGES).AndReturn(123) self.mox.ReplayAll() self.assertEqual(123, common.IsPanicModeNoPackages()) self.mox.VerifyAll()
def testGetComputerManifestWhenEmptyDynamic(self): """Test ComputerInstallsPending().""" uuid = 'uuid' last_notified_datetime = self.mox.CreateMockAnything() client_id = { 'uuid': 'uuid', 'owner': 'owner', 'hostname': 'hostname', 'serial': 'serial', 'config_track': 'config_track', 'track': 'track', 'site': 'site', 'office': 'office', 'os_version': 'os_version', 'client_version': 'client_version', 'on_corp': True, 'last_notified_datetime': last_notified_datetime, 'uptime': None, 'root_disk_free': None, 'user_disk_free': None, } computer = test.GenericContainer(**client_id) computer.connections_on_corp = 2 computer.connections_off_corp = 1 computer.user_settings = None # PackageInfo entities package_infos = [ test.GenericContainer(plist='plistOtherPackage', version='1.0'), test.GenericContainer(plist='plistPackageInstalled1', version='1.0'), test.GenericContainer(plist='plistPackageInstalled2', version='1.0'), test.GenericContainer(plist='plistPackageNotInstalled1', version='1.0'), ] packagemap = {} self.mox.StubOutWithMock(common.models, 'Computer') self.mox.StubOutWithMock(common, 'IsPanicModeNoPackages') self.mox.StubOutWithMock(common.models, 'Manifest') self.mox.StubOutWithMock(common, 'GenerateDynamicManifest') self.mox.StubOutWithMock(common.plist_module, 'MunkiManifestPlist') self.mox.StubOutWithMock(common.models, 'PackageInfo') self.mox.StubOutWithMock(common.plist_module, 'MunkiPackageInfoPlist') # mock manifest creation common.models.Computer.get_by_key_name(uuid).AndReturn(computer) common.IsPanicModeNoPackages().AndReturn(False) mock_plist = self.mox.CreateMockAnything() common.models.Manifest.MemcacheWrappedGet('track').AndReturn( test.GenericContainer(enabled=True, plist=mock_plist)) common.GenerateDynamicManifest( mock_plist, client_id, user_settings=None).AndReturn(None) self.mox.ReplayAll() self.assertRaises( common.ManifestNotFoundError, common.GetComputerManifest, uuid=uuid) self.mox.VerifyAll()
def GetReportFeedback(self, uuid, report_type, **kwargs): """Inspect a report and provide a feedback status/command. Args: uuid: str, computer uuid report_type: str, report type **kwargs: dict, additional report parameters, e.g: on_corp: str, optional, '1' or '0', on_corp status message: str, optional, message from client details: str, optional, details from client ip_address: str, optional, IP address of client Returns: common.ReportFeedback.* constant """ feedback = {} if 'computer' in kwargs: c = kwargs['computer'] else: c = models.Computer.get_by_key_name(uuid) ip_address = kwargs.get('ip_address', None) client_exit = kwargs.get('client_exit', None) if client_exit and report_type == 'preflight': # client has requested an exit, but let's ensure we should allow it. if c is None or c.postflight_datetime is None: # client has never fully executed Munki. feedback['force_continue'] = True else: # check if the postflight_datetime warrants a FORCE_CONTINUE now = datetime.datetime.utcnow() postflight_stale_datetime = now - datetime.timedelta( days=FORCE_CONTINUE_POSTFLIGHT_DAYS) if c.postflight_datetime < postflight_stale_datetime: # client hasn't executed Munki in FORCE_CONTINUE_POSTFLIGHT_DAYS. feedback['force_continue'] = True else: feedback['exit'] = True elif report_type == 'preflight': if IsExitFeedbackIpAddress(ip_address): feedback['exit'] = True elif common.IsPanicModeNoPackages(): feedback['exit'] = True elif not c or c.preflight_datetime is None: # this is the first preflight post from this client. feedback['force_continue'] = True elif getattr(c, 'upload_logs_and_notify', None) is not None: feedback['logging_level'] = 3 feedback['upload_logs'] = True else: # check if preflight_count_since_postflight warrants a repair. if (c.preflight_count_since_postflight >= REPAIR_CLIENT_PREFLIGHT_COUNT_SINCE_POSTFLIGHT): feedback['pkill_installd'] = True feedback['pkill_softwareupdated'] = True feedback['repair'] = True feedback['logging_level'] = 3 feedback['upload_logs'] = True return feedback
def testGetComputerManifestIsPanicMode(self): """Test ComputerInstallsPending().""" uuid = 'uuid' last_notified_datetime = self.mox.CreateMockAnything() client_id = { 'uuid': 'uuid', 'owner': 'owner', 'hostname': 'hostname', 'serial': 'serial', 'config_track': 'config_track', 'track': 'track', 'site': 'site', 'office': 'office', 'os_version': 'os_version', 'client_version': 'client_version', 'on_corp': True, 'last_notified_datetime': last_notified_datetime, 'uptime': None, 'root_disk_free': None, 'user_disk_free': None, } computer = test.GenericContainer(**client_id) computer.connections_on_corp = 2 computer.connections_off_corp = 1 computer.user_settings = None # PackageInfo entities package_infos = [ test.GenericContainer(plist='plistOtherPackage', version='1.0'), test.GenericContainer(plist='plistPackageInstalled1', version='1.0'), test.GenericContainer(plist='plistPackageInstalled2', version='1.0'), test.GenericContainer(plist='plistPackageNotInstalled1', version='1.0'), ] packagemap = {} self.mox.StubOutWithMock(common.models, 'Computer') self.mox.StubOutWithMock(common, 'IsPanicModeNoPackages') common.models.Computer.get_by_key_name(uuid).AndReturn(computer) common.IsPanicModeNoPackages().AndReturn(True) manifest_expected = '%s%s' % ( common.plist_module.PLIST_HEAD, common.plist_module.PLIST_FOOT) self.mox.ReplayAll() manifest = common.GetComputerManifest(uuid=uuid) self.assertEqual(manifest, manifest_expected) self.mox.VerifyAll()
def testGetComputerManifestWhenManifestNotFound(self): """Test ComputerInstallsPending().""" uuid = 'uuid' last_notified_datetime = self.mox.CreateMockAnything() client_id = { 'uuid': 'uuid', 'owner': 'owner', 'hostname': 'hostname', 'serial': 'serial', 'config_track': 'config_track', 'track': 'track', 'site': 'site', 'office': 'office', 'os_version': 'os_version', 'client_version': 'client_version', 'on_corp': True, 'last_notified_datetime': last_notified_datetime, 'uptime': None, 'root_disk_free': None, 'user_disk_free': None, } computer = test.GenericContainer(**client_id) computer.connections_on_corp = 2 computer.connections_off_corp = 1 computer.user_settings = None packagemap = {} self.mox.StubOutWithMock(common.models, 'Computer') self.mox.StubOutWithMock(common, 'IsPanicModeNoPackages') self.mox.StubOutWithMock(common.models, 'Manifest') # mock manifest creation common.models.Computer.get_by_key_name(uuid).AndReturn(computer) common.IsPanicModeNoPackages().AndReturn(False) common.models.Manifest.MemcacheWrappedGet('track').AndReturn(None) self.mox.ReplayAll() self.assertRaises( common.ManifestNotFoundError, common.GetComputerManifest, uuid=uuid) self.mox.VerifyAll()
def get(self, filename): """GET Args: filename: str, package filename like 'foo.dmg' Returns: None if a blob is being returned, or a response object """ auth_return = auth.DoAnyAuth() if hasattr(auth_return, 'email'): email = auth_return.email() if not auth.IsAdminUser(email) and not auth.IsSupportUser(email): raise auth.IsAdminMismatch filename = urllib.unquote(filename) pkg = models.PackageInfo.MemcacheWrappedGet(filename) if pkg is None or not pkg.blobstore_key: self.error(404) return if common.IsPanicModeNoPackages(): self.error(503) return # Get the Blobstore BlobInfo for this package; memcache wrapped. memcache_key = 'blobinfo_%s' % filename blob_info = memcache.get(memcache_key) if not blob_info: blob_info = blobstore.BlobInfo.get(pkg.blobstore_key) if blob_info: memcache.set(memcache_key, blob_info, 300) # cache for 5 minutes. else: logging.critical( 'Failure fetching BlobInfo for %s. Verify the blob exists: %s', pkg.filename, pkg.blobstore_key) self.error(404) return header_date_str = self.request.headers.get('If-Modified-Since', '') etag_nomatch_str = self.request.headers.get('If-None-Match', 0) etag_match_str = self.request.headers.get('If-Match', 0) pkg_date = blob_info.creation pkg_size_bytes = blob_info.size # TODO(user): The below can be simplified once all of our clients # have ETag values set on the filesystem for these files. The # parsing of If-Modified-Since could be removed. Removing it prematurely # will cause a re-download of all packages on all clients for 1 iteration # until they all have ETag values. # Reduce complexity of elif conditional below. # If an If-None-Match: ETag is supplied, don't worry about a # missing file modification date -- the ETag supplies everything needed. if etag_nomatch_str and not header_date_str: resource_expired = False else: resource_expired = handlers.IsClientResourceExpired( pkg_date, header_date_str) # Client supplied If-Match: etag, but that etag does not match current # etag. return 412. if (etag_match_str and pkg.pkgdata_sha256 and etag_match_str != pkg.pkgdata_sha256): self.response.set_status(412) # Client supplied no etag or If-No-Match: etag, and the etag did not # match, or the client's file is older than the mod time of this package. elif ((etag_nomatch_str and pkg.pkgdata_sha256 and etag_nomatch_str != pkg.pkgdata_sha256) or resource_expired): self.response.headers['Content-Disposition'] = str( 'attachment; filename=%s' % filename) # header date empty or package has changed, send blob with last-mod date. if pkg.pkgdata_sha256: self.response.headers['ETag'] = str(pkg.pkgdata_sha256) self.response.headers['Last-Modified'] = pkg_date.strftime( handlers.HEADER_DATE_FORMAT) self.response.headers['X-Download-Size'] = str(pkg_size_bytes) self.send_blob(pkg.blobstore_key) else: # Client doesn't need to do anything, current version is OK based on # ETag and/or last modified date. if pkg.pkgdata_sha256: self.response.headers['ETag'] = str(pkg.pkgdata_sha256) self.response.set_status(304)
def testGetComputerManifest(self): """Test ComputerInstallsPending().""" uuid = 'uuid' last_notified_datetime = self.mox.CreateMockAnything() client_id = { 'uuid': 'uuid', 'owner': 'owner', 'hostname': 'hostname', 'serial': 'serial', 'config_track': 'config_track', 'track': 'track', 'site': 'site', 'office': 'office', 'os_version': 'os_version', 'client_version': 'client_version', 'on_corp': True, 'last_notified_datetime': last_notified_datetime, 'uptime': None, 'root_disk_free': None, 'user_disk_free': None, } computer = test.GenericContainer(**client_id) computer.connections_on_corp = 2 computer.connections_off_corp = 1 computer.user_settings = None # PackageInfo entities mock_pl1 = self.mox.CreateMockAnything() mock_pl2 = self.mox.CreateMockAnything() mock_pl3 = self.mox.CreateMockAnything() mock_pl4 = self.mox.CreateMockAnything() package_infos = [ test.GenericContainer(plist=mock_pl1, version='1.0', name='fooname1'), test.GenericContainer(plist=mock_pl2, version='1.0', name='fooname2'), test.GenericContainer(plist=mock_pl3, version='1.0', name='fooname3'), test.GenericContainer(plist=mock_pl4, version='1.0', name='fooname4'), ] packagemap = {} self.mox.StubOutWithMock(common.models, 'Computer') self.mox.StubOutWithMock(common, 'IsPanicModeNoPackages') self.mox.StubOutWithMock(common.models, 'Manifest') self.mox.StubOutWithMock(common, 'GenerateDynamicManifest') self.mox.StubOutWithMock(common.plist_module, 'MunkiManifestPlist') self.mox.StubOutWithMock(common.models, 'PackageInfo') self.mox.StubOutWithMock(common.plist_module, 'MunkiPackageInfoPlist') # mock manifest creation common.models.Computer.get_by_key_name(uuid).AndReturn(computer) common.IsPanicModeNoPackages().AndReturn(False) mock_plist = self.mox.CreateMockAnything() common.models.Manifest.MemcacheWrappedGet('track').AndReturn( test.GenericContainer(enabled=True, plist=mock_plist)) common.GenerateDynamicManifest( mock_plist, client_id, user_settings=None).AndReturn( 'manifest_plist') # mock manifest parsing mock_manifest_plist = self.mox.CreateMockAnything() common.plist_module.MunkiManifestPlist('manifest_plist').AndReturn( mock_manifest_plist) mock_manifest_plist.Parse().AndReturn(None) # mock manifest reading and package map creation mock_package_info = self.mox.CreateMockAnything() common.models.PackageInfo.all().AndReturn(mock_package_info) iter_return = [] for package_info in package_infos: iter_return.append(test.GenericContainer( plist=package_info.plist, name=package_info.name)) package_info.plist.get('display_name', None).AndReturn(None) package_info.plist.get('name').AndReturn(package_info.name) package_info.plist.get('version', '').AndReturn(package_info.version) packagemap[package_info.name] = '%s-%s' % ( package_info.name, package_info.version) def __iter_func(): for i in iter_return: yield i mock_package_info.__iter__().AndReturn(__iter_func()) manifest_expected = { 'plist': mock_manifest_plist, 'packagemap': packagemap, } self.mox.ReplayAll() manifest = common.GetComputerManifest(uuid=uuid, packagemap=True) self.assertEqual(manifest, manifest_expected) self.mox.VerifyAll()
def GetReportFeedback(self, uuid, report_type, **kwargs): """Inspect a report and provide a feedback status/command. Args: uuid: str, computer uuid report_type: str, report type kwargs: dict, additional report parameters, e.g: on_corp: str, optional, '1' or '0', on_corp status message: str, optional, message from client details: str, optional, details from client ip_address: str, optional, IP address of client Returns: common.ReportFeedback.* constant """ report = common.ReportFeedback.OK if 'computer' in kwargs: c = kwargs['computer'] else: c = models.Computer.get_by_key_name(uuid) ip_address = kwargs.get('ip_address', None) client_exit = kwargs.get('client_exit', None) # TODO(user): if common.BusinessLogicMethod ... if client_exit and report_type == 'preflight': report = common.ReportFeedback.EXIT # client has requested an exit, but let's ensure we should allow it. if c is None or c.postflight_datetime is None: # client has never fully executed Munki. report = common.ReportFeedback.FORCE_CONTINUE else: # check if the postflight_datetime warrants a FORCE_CONTINUE now = datetime.datetime.utcnow() postflight_stale_datetime = now - datetime.timedelta( days=FORCE_CONTINUE_POSTFLIGHT_DAYS) if c.postflight_datetime < postflight_stale_datetime: # client hasn't executed Munki in FORCE_CONTINUE_POSTFLIGHT_DAYS. report = common.ReportFeedback.FORCE_CONTINUE elif report_type == 'preflight': if IsExitFeedbackIpAddress(ip_address): report = common.ReportFeedback.EXIT elif common.IsPanicModeNoPackages(): report = common.ReportFeedback.EXIT elif not c or c.preflight_datetime is None: # this is the first preflight post from this client. report = common.ReportFeedback.FORCE_CONTINUE elif getattr(c, 'upload_logs_and_notify', None) is not None: report = common.ReportFeedback.UPLOAD_LOGS elif c.postflight_datetime is None: # client has posted preflight before, but not postflight report = common.ReportFeedback.REPAIR else: # check if postflight_datetime warrants a repair. pre_post_timedelta = c.preflight_datetime - c.postflight_datetime if pre_post_timedelta > datetime.timedelta( days=REPAIR_CLIENT_PRE_POST_DIFF_DAYS): report = common.ReportFeedback.REPAIR if report not in [ common.ReportFeedback.OK, common.ReportFeedback.FORCE_CONTINUE ]: logging.info('Feedback to %s: %s', uuid, report) return report