def test_stop_between_download_and_stage1(self): print 'test_stop_between_download_and_stage1' global settings current_time = time.time() settings = change_settings("startTime", time.strftime('%H:%M', time.localtime(current_time + 180))) settings = change_settings("category", "importantandrecommended") old_log_len = len(waagent.GetFileContents(log_file)) delta_time = int(time.strftime('%S', time.localtime(current_time + 120))) with self.assertRaises(SystemExit) as cm: enable() self.assertEqual(cm.exception.code, 0) # set stop flag after downloaded 40 seconds time.sleep(160 - delta_time) os.remove('mrseq') settings = change_settings("stop", "true") with self.assertRaises(SystemExit) as cm: enable() self.assertEqual(cm.exception.code, 0) self.assertTrue(MyPatching.exists_stop_flag()) # Make sure the total sleep time is greater than 180s time.sleep(20 + delta_time + 5 + 60) self.assertFalse(MyPatching.exists_stop_flag()) download_list = get_patch_list(MyPatching.package_downloaded_path) self.assertEqual(download_list, ['a', 'b', 'c', 'd', 'e', '1', '2', '3', '4']) self.assertFalse(waagent.GetFileContents(MyPatching.package_patched_path)) log_contents = waagent.GetFileContents(log_file)[old_log_len:] self.assertTrue('Installing patches is stopped/canceled' in log_contents) restore_settings()
def test_stop_before_download(self): ''' check stop flag before download and after patch ''' print 'test_stop_before_download' old_log_len = len(waagent.GetFileContents(log_file)) current_time = time.time() protect_settings['startTime'] = time.strftime( '%H:%M', time.localtime(current_time + 180)) MyPatching.download_duration = 60 with self.assertRaises(SystemExit) as cm: enable() self.assertEqual(cm.exception.code, 0) os.remove('mrseq') protect_settings['stop'] = 'true' with self.assertRaises(SystemExit) as cm: enable() self.assertEqual(cm.exception.code, 0) self.assertTrue(MyPatching.exists_stop_flag()) time.sleep(180 + 5) self.assertFalse(MyPatching.exists_stop_flag()) self.assertFalse( waagent.GetFileContents(MyPatching.package_downloaded_path)) self.assertFalse( waagent.GetFileContents(MyPatching.package_patched_path)) log_contents = waagent.GetFileContents(log_file)[old_log_len:] self.assertTrue( 'Downloading patches is stopped/canceled' in log_contents)
def test_stop_between_download_and_stage1(self): print 'test_stop_between_download_and_stage1' old_log_len = len(waagent.GetFileContents(log_file)) current_time = time.time() protect_settings['startTime'] = time.strftime( '%H:%M', time.localtime(current_time + 180)) delta_time = int( time.strftime('%S', time.localtime(current_time + 120))) MyPatching.download_duration = 60 with self.assertRaises(SystemExit) as cm: enable() self.assertEqual(cm.exception.code, 0) # set stop flag after downloaded 40 seconds time.sleep(160 - delta_time) os.remove('mrseq') protect_settings['stop'] = 'true' with self.assertRaises(SystemExit) as cm: enable() self.assertEqual(cm.exception.code, 0) self.assertTrue(MyPatching.exists_stop_flag()) # Make sure the total sleep time is greater than 180s time.sleep(20 + delta_time + 5) self.assertFalse(MyPatching.exists_stop_flag()) download_list = get_patch_list(MyPatching.package_downloaded_path) self.assertEqual(download_list, ['a', 'b', 'c', 'd', 'e', '1', '2', '3', '4']) self.assertFalse( waagent.GetFileContents(MyPatching.package_patched_path)) log_contents = waagent.GetFileContents(log_file)[old_log_len:] self.assertTrue( 'Installing patches is stopped/canceled' in log_contents)
def test_patch_one_off(self): ''' check package.downloaded and package.patched when patch_one_off successful ''' print 'test_patch_one_off' protect_settings['startTime'] = '' with self.assertRaises(SystemExit) as cm: enable() self.assertEqual(cm.exception.code, 0) self.assertEqual(get_status("Enable"), 'success') security_download_list = get_patch_list( MyPatching.package_downloaded_path, 'Important') self.assertTrue( set(security_download_list) == set( MyPatching.security_download_list)) all_download_list = get_patch_list(MyPatching.package_patched_path) self.assertTrue( set(all_download_list) == set(MyPatching.all_download_list)) download_content = waagent.GetFileContents( MyPatching.package_downloaded_path) patch_content = waagent.GetFileContents( MyPatching.package_patched_path) self.assertEqual(patch_content, download_content)
def test_patch_time_exceed(self): ''' check package.patched when patch time exceed ''' print 'test_patch_time_exceed' old_log_len = len(waagent.GetFileContents(log_file)) def patch_package(self): time.sleep(11) return 0 MyPatching.patch_package = patch_package # 5 minutes reserved for reboot protect_settings['installDuration'] = '00:06' with self.assertRaises(SystemExit) as cm: download() self.assertEqual(cm.exception.code, 0) with self.assertRaises(SystemExit) as cm: patch() self.assertEqual(cm.exception.code, 0) patch_list = get_patch_list(MyPatching.package_patched_path) self.assertEqual(patch_list, ['a', 'b', 'c', 'd', 'e', '1']) log_contents = waagent.GetFileContents(log_file)[old_log_len:] self.assertTrue('Patching time exceeded' in log_contents)
def test_patch_failed(self): ''' check file package.patched when patch fail ''' print 'test_patch_failed' global settings settings = {} def patch_package(self): return 1 MyPatching.patch_package = patch_package old_log_len = len(waagent.GetFileContents(log_file)) with self.assertRaises(SystemExit) as cm: download() self.assertEqual(cm.exception.code, 0) with self.assertRaises(SystemExit) as cm: patch() log_contents = waagent.GetFileContents(log_file)[old_log_len:] self.assertEqual(cm.exception.code, 0) patch_content = waagent.GetFileContents( MyPatching.package_patched_path) self.assertFalse(patch_content) self.assertTrue('Failed to patch the package' in log_contents)
def test_stop_before_download(self): ''' check stop flag before download and after patch ''' print('test_stop_before_download') global settings current_time = time.time() settings = change_settings( "startTime", time.strftime('%H:%M', time.localtime(current_time + 180))) settings = change_settings("category", "importantandrecommended") old_log_len = len(waagent.GetFileContents(log_file)) with self.assertRaises(SystemExit) as cm: enable() self.assertEqual(cm.exception.code, 0) os.remove('mrseq') settings = change_settings("stop", "true") with self.assertRaises(SystemExit) as cm: enable() self.assertEqual(cm.exception.code, 0) self.assertTrue(MyPatching.exists_stop_flag()) time.sleep(180 + 5 + 60) self.assertFalse(MyPatching.exists_stop_flag()) self.assertFalse( waagent.GetFileContents(MyPatching.package_downloaded_path)) self.assertFalse( waagent.GetFileContents(MyPatching.package_patched_path)) log_contents = waagent.GetFileContents(log_file)[old_log_len:] self.assertTrue( 'Downloading patches is stopped/canceled' in log_contents) restore_settings()
def try_parse_context(self): self._context = HandlerContext(self._short_name) handler_env = None config = None ctxt = None code = 0 # get the HandlerEnvironment.json. According to the extension handler # spec, it is always in the ./ directory self.log('cwd is ' + os.path.realpath(os.path.curdir)) handler_env_file = './HandlerEnvironment.json' if not os.path.isfile(handler_env_file): self.error("Unable to locate " + handler_env_file) return None ctxt = waagent.GetFileContents(handler_env_file) if ctxt == None: self.error("Unable to read " + handler_env_file) try: handler_env = json.loads(ctxt) except: pass if handler_env == None: self.log("JSON error processing " + handler_env_file) return None if type(handler_env) == list: handler_env = handler_env[0] self._context._name = handler_env['name'] self._context._version = str(handler_env['version']) self._context._config_dir = handler_env['handlerEnvironment'][ 'configFolder'] self._context._log_dir = handler_env['handlerEnvironment']['logFolder'] self._context._log_file = os.path.join( handler_env['handlerEnvironment']['logFolder'], 'extension.log') self._change_log_file() self._context._status_dir = handler_env['handlerEnvironment'][ 'statusFolder'] self._context._heartbeat_file = handler_env['handlerEnvironment'][ 'heartbeatFile'] self._context._seq_no = self._get_current_seq_no( self._context._config_dir) if self._context._seq_no < 0: self.error("Unable to locate a .settings file!") return None self._context._seq_no = str(self._context._seq_no) self.log('sequence number is ' + self._context._seq_no) self._context._status_file = os.path.join( self._context._status_dir, self._context._seq_no + '.status') self._context._settings_file = os.path.join( self._context._config_dir, self._context._seq_no + '.settings') self.log("setting file path is" + self._context._settings_file) ctxt = None ctxt = waagent.GetFileContents(self._context._settings_file) if ctxt == None: error_msg = 'Unable to read ' + self._context._settings_file + '. ' self.error(error_msg) return None self.log("JSON config: " + ctxt) self._context._config = self._parse_config(ctxt) return self._context
def test_patch_one_off(self): ''' check package.downloaded and package.patched when patch_one_off successful ''' print 'test_patch_one_off' global settings settings = {"oneoff": "true", "category": "importantandrecommended"} with self.assertRaises(SystemExit) as cm: oneoff() self.assertEqual(cm.exception.code, 0) self.assertEqual(get_status("Enable"), 'success') time.sleep(3) security_download_list = get_patch_list( MyPatching.package_downloaded_path, 'important') self.assertTrue( set(security_download_list) == set( MyPatching.security_download_list)) all_download_list = get_patch_list(MyPatching.package_patched_path) self.assertTrue( set(all_download_list) == set(MyPatching.all_download_list)) download_content = waagent.GetFileContents( MyPatching.package_downloaded_path) patch_content = waagent.GetFileContents( MyPatching.package_patched_path) self.assertEqual(patch_content, download_content)
def test_stop_between_stage1_and_stage2(self): """ Manually add MyPathing.gap_between_stage = 20 """ print 'test_stop_between_stage1_and_stage2' old_log_len = len(waagent.GetFileContents(log_file)) current_time = time.time() protect_settings['startTime'] = time.strftime('%H:%M', time.localtime(current_time + 180)) delta_time = int(time.strftime('%S', time.localtime(current_time))) MyPatching.download_duration = 60 with self.assertRaises(SystemExit) as cm: enable() self.assertEqual(cm.exception.code, 0) # Set stop flag after patched 10 seconds # Meanwhile the extension is sleeping between stage 1 & 2 time.sleep(180 - delta_time + 10) os.remove('mrseq') protect_settings['stop'] = 'true' with self.assertRaises(SystemExit) as cm: enable() self.assertEqual(cm.exception.code, 0) self.assertTrue(MyPatching.exists_stop_flag()) # The patching (stage 1 & 2) has ended time.sleep(20) self.assertFalse(MyPatching.exists_stop_flag()) download_list = get_patch_list(MyPatching.package_downloaded_path) self.assertEqual(download_list, ['a', 'b', 'c', 'd', 'e', '1', '2', '3', '4']) patch_list = get_patch_list(MyPatching.package_patched_path) self.assertEqual(patch_list, ['a', 'b', 'c', 'd', 'e']) log_contents = waagent.GetFileContents(log_file)[old_log_len:] self.assertTrue("Installing patches (Category:" + MyPatching.category_all + ") is stopped/canceled" in log_contents)
def test_patch_time_exceed(self): ''' check package.patched when patch time exceed ''' print 'test_patch_time_exceed' global settings settings = { "category": "importantandrecommended", "installDuration": "00:06" # 5 minutes reserved for reboot } old_log_len = len(waagent.GetFileContents(log_file)) def patch_package(self): time.sleep(11) return 0 MyPatching.patch_package = patch_package with self.assertRaises(SystemExit) as cm: download() self.assertEqual(cm.exception.code, 0) with self.assertRaises(SystemExit) as cm: patch() self.assertEqual(cm.exception.code, 0) patch_list = get_patch_list(MyPatching.package_patched_path) self.assertEqual(patch_list, ['a', 'b', 'c', 'd', 'e', '1']) log_contents = waagent.GetFileContents(log_file)[old_log_len:] self.assertTrue('Patching time exceeded' in log_contents)
def test_stop_between_stage1_and_stage2(self): print 'test_stop_between_stage1_and_stage2' global settings current_time = time.time() settings = change_settings("startTime", time.strftime('%H:%M', time.localtime(current_time + 180))) settings = change_settings("category", "importantandrecommended") old_log_len = len(waagent.GetFileContents(log_file)) delta_time = int(time.strftime('%S', time.localtime(current_time))) with self.assertRaises(SystemExit) as cm: enable() self.assertEqual(cm.exception.code, 0) # Set stop flag after patched 10 seconds # Meanwhile the extension is sleeping between stage 1 & 2 time.sleep(180 - delta_time + 10) os.remove('mrseq') settings = change_settings("stop", "true") with self.assertRaises(SystemExit) as cm: enable() self.assertEqual(cm.exception.code, 0) self.assertTrue(MyPatching.exists_stop_flag()) # The patching (stage 1 & 2) has ended time.sleep(20) self.assertFalse(MyPatching.exists_stop_flag()) download_list = get_patch_list(MyPatching.package_downloaded_path) self.assertEqual(download_list, ['a', 'b', 'c', 'd', 'e', '1', '2', '3', '4']) patch_list = get_patch_list(MyPatching.package_patched_path) self.assertEqual(patch_list, ['a', 'b', 'c', 'd', 'e']) log_contents = waagent.GetFileContents(log_file)[old_log_len:] self.assertTrue("Installing patches (Category:" + MyPatching.category_all + ") is stopped/canceled" in log_contents) restore_settings()
def test_download_time_exceed(self): ''' check package.downloaded and package.patched ''' print('test_download_time_exceed') global settings current_time = time.time() settings = change_settings( "startTime", time.strftime('%H:%M', time.localtime(current_time + 180))) settings = change_settings("category", "importantandrecommended") old_log_len = len(waagent.GetFileContents(log_file)) with self.assertRaises(SystemExit) as cm: enable() self.assertEqual(cm.exception.code, 0) time.sleep(180 + 10) all_download_list = get_patch_list(MyPatching.package_downloaded_path) self.assertTrue( set(all_download_list) == set(['a', 'b', 'c', 'd', 'e'])) # Check extension.log log_contents = waagent.GetFileContents(log_file)[old_log_len:] self.assertTrue('Download time exceeded' in log_contents) restore_settings()
def get_pkg_to_patch(self, category): patchlist = [ line.split()[0] for line in waagent.GetFileContents( self.package_downloaded_path).split('\n') if line.endswith(category) ] return patchlist
def get_patch_list(file_path, category = None): content = waagent.GetFileContents(file_path) if category != None: result = [line.split()[0] for line in content.split('\n') if line.endswith(category)] else: result = [line.split()[0] for line in content.split('\n') if ' ' in line] return result
def _get_other_sudoers(userName): sudoersFile = '/etc/sudoers.d/waagent' if not os.path.isfile(sudoersFile): return None sudoers = waagent.GetFileContents(sudoersFile).split("\n") sudoers = filter(lambda x: userName not in x, sudoers) return sudoers
def enable(hutil): pidFile = os.path.join(aem.LibDir, "pid"); #Check whether monitor process is running. #If it does, return. Otherwise clear pid file if os.path.isfile(pidFile): pid = waagent.GetFileContents(pidFile) if os.path.isdir(os.path.join("/proc", pid)): if hutil.is_seq_smaller(): hutil.do_exit(0, 'Enable', 'success', '0', 'Azure Enhanced Monitor is already running') else: waagent.Log("Stop old daemon: {0}".format(pid)) os.kill(int(pid), 9) os.remove(pidFile) args = [os.path.join(os.getcwd(), __file__), "daemon"] devnull = open(os.devnull, 'w') child = subprocess.Popen(args, stdout=devnull, stderr=devnull) if child.pid == None or child.pid < 1: hutil.do_exit(1, 'Enable', 'error', '1', 'Failed to launch Azure Enhanced Monitor') else: hutil.save_seq() waagent.SetFileContents(pidFile, str(child.pid)) waagent.Log(("Daemon pid: {0}").format(child.pid)) hutil.do_exit(0, 'Enable', 'success', '0', 'Azure Enhanced Monitor is enabled')
def _get_most_recent_seq(self): if (os.path.isfile('mrseq')): seq = waagent.GetFileContents('mrseq') if (seq): return int(seq) return -1
def set_download_cron(self): script_file_path = os.path.realpath(sys.argv[0]) script_dir = os.path.dirname(script_file_path) script_file = os.path.basename(script_file_path) old_line_end = ' '.join([script_file, '-download']) if self.disabled: new_line = '\n' else: if self.download_time > self.start_time: dow = ','.join( [str((day - 1) % 7) for day in self.day_of_week]) else: dow = ','.join([str(day % 7) for day in self.day_of_week]) hr = str(self.download_time.hour) minute = str(self.download_time.minute) new_line = ' '.join([ '\n' + minute, hr, '* *', dow, 'root cd', script_dir, '&& python check.py', self.interval_of_weeks, '&& python', script_file, '-download > /dev/null 2>&1\n' ]) waagent.ReplaceFileContentsAtomic( self.crontab, '\n'.join( filter(lambda a: a and (old_line_end not in a), waagent.GetFileContents(self.crontab).split('\n'))) + new_line)
def is_valid_nonquery(self, settings_file_path): # note: the nonquery operations list includes update and disable nonquery_ops = [ CommonVariables.EnableEncryption, CommonVariables.EnableEncryptionFormat, CommonVariables.EnableEncryptionFormatAll, CommonVariables.UpdateEncryptionSettings, CommonVariables.DisableEncryption ] if settings_file_path and os.path.exists(settings_file_path): # open file and look for presence of nonquery operation config_txt = waagent.GetFileContents(settings_file_path) config_obj = self._parse_config(config_txt) public_settings_str = config_obj['runtimeSettings'][0][ 'handlerSettings'].get('publicSettings') # if not json already, load string as json if isinstance(public_settings_str, basestring): public_settings = json.loads(public_settings_str) else: public_settings = public_settings_str operation = public_settings.get( CommonVariables.EncryptionEncryptionOperationKey) if operation and (operation in nonquery_ops): return True # invalid input, or not recognized as a valid nonquery operation return False
def test_download(self): """ Check file package.downloaded after download """ print 'test_download' global settings settings = { "category": "importantandrecommended", } with self.assertRaises(SystemExit) as cm: download() self.assertEqual(cm.exception.code, 0) download_content = waagent.GetFileContents( MyPatching.package_downloaded_path) security_download_list = get_patch_list( MyPatching.package_downloaded_path, 'important') self.assertTrue( set(security_download_list) == set( MyPatching.security_download_list)) all_download_list = get_patch_list(MyPatching.package_downloaded_path) self.assertTrue( set(all_download_list) == set(MyPatching.all_download_list))
def download(self): # Read the latest configuration for scheduled task settings = json.loads( waagent.GetFileContents(self.scheduled_configs_file)) self.parse_settings(settings) self.provide_vm_status_test(StatusTest["Scheduled"]) if not self.check_vm_idle(StatusTest["Scheduled"]): return if self.exists_stop_flag(): self.hutil.log_and_syslog( logging.INFO, "Downloading patches is stopped/canceled") return waagent.SetFileContents(self.package_downloaded_path, '') waagent.SetFileContents(self.package_patched_path, '') start_download_time = time.time() # Installing security patches is mandatory self._download(self.category_required) if self.category == self.category_all: self._download(self.category_all) end_download_time = time.time() waagent.AddExtensionEvent( name=self.hutil.get_name(), op=waagent.WALAEventOperation.Download, isSuccess=True, version=Version, message=" ".join([ "Real downloading time is", str(round(end_download_time - start_download_time, 3)), "s" ]))
def set_patch_cron(self): script_file_path = os.path.realpath(sys.argv[0]) script_dir = os.path.dirname(script_file_path) script_file = os.path.basename(script_file_path) old_line_end = ' '.join([script_file, '-patch']) if self.disabled: new_line = '\n' else: hr = str(self.start_time.hour) minute = str(self.start_time.minute) minute_cleanup = str(self.start_time.minute + 1) dow = ','.join([str(day) for day in self.day_of_week]) new_line = ' '.join([ '\n' + minute, hr, '* *', dow, 'root cd', script_dir, '&& python check.py', self.interval_of_weeks, '&& python', script_file, '-patch >/dev/null 2>&1\n' ]) new_line += ' '.join([ minute_cleanup, hr, '* *', dow, 'root rm -f', self.stop_flag_path, '\n' ]) waagent.ReplaceFileContentsAtomic( self.crontab, "\n".join( filter( lambda a: a and (old_line_end not in a) and (self.stop_flag_path not in a), waagent.GetFileContents(self.crontab).split('\n'))) + new_line)
def redo_current_status(self): stat_rept = waagent.GetFileContents(self._context._status_file) stat = json.loads(stat_rept) self.do_status_report(stat[0]["status"]["operation"], stat[0]["status"]["status"], stat[0]["status"]["code"], stat[0]["status"]["formattedMessage"]["message"])
def test_patch(self): ''' check file package.patched when patch successful ''' print 'test_patch' with self.assertRaises(SystemExit) as cm: download() self.assertEqual(cm.exception.code, 0) with self.assertRaises(SystemExit) as cm: patch() self.assertEqual(cm.exception.code, 0) download_content = waagent.GetFileContents( MyPatching.package_downloaded_path) patch_content = waagent.GetFileContents( MyPatching.package_patched_path) self.assertEqual(download_content, patch_content)
def get_pkg_patched(self): if not os.path.isfile(self.package_patched_path): return [] pkg_patched = waagent.GetFileContents(self.package_patched_path) if not pkg_patched: return [] patchedlist = [ line.split()[0] for line in pkg_patched.split('\n') if line ] return patchedlist
def test_cron(self): print 'test_cron' enable_time = time.time() protect_settings['startTime'] = time.strftime( '%H:%M', time.localtime(enable_time + 180)) delta_time = int(time.strftime('%S', time.localtime(enable_time + 120))) MyPatching.download_duration = 60 with self.assertRaises(SystemExit) as cm: enable() self.assertEqual(cm.exception.code, 0) self.assertEqual(get_status("Enable"), 'success') download_cmd = 'python test_handler.py -download' patch_cmd = 'python test_handler.py -patch' crontab_content = waagent.GetFileContents('/etc/crontab') self.assertTrue(download_cmd in crontab_content) self.assertTrue(patch_cmd in crontab_content) time.sleep(180 + 5) distro = DistInfo()[0] if 'SuSE' in distro: find_cron = 'grep CRON /var/log/messages' elif 'Ubuntu' in distro: find_cron = 'grep CRON /var/log/syslog' else: find_cron = 'cat /var/log/cron' if 'SuSE' in distro: find_download_time = "grep '" + time.strftime( '%Y-%m-%dT%H:%M', time.localtime(enable_time + 120)) + "'" find_patch_time = "grep '" + time.strftime( '%Y-%m-%dT%H:%M', time.localtime(enable_time + 180)) + "'" else: day = int(time.strftime('%d', time.localtime(enable_time))) find_download_time = "grep '" + str(day) + time.strftime( ' %H:%M', time.localtime(enable_time + 120)) + "'" find_patch_time = "grep '" + str(day) + time.strftime( ' %H:%M', time.localtime(enable_time + 180)) + "'" find_download = "grep 'python test_handler.py -download'" find_patch = "grep 'python test_handler.py -patch'" retcode, output = waagent.RunGetOutput(find_cron + ' | ' + find_download_time + ' | ' + find_download) self.assertTrue(output) retcode, output = waagent.RunGetOutput(find_cron + ' | ' + find_patch_time + ' | ' + find_patch) self.assertTrue(output)
def test_reset_sshd_config(self): path = '/tmp/sshd_config' resources = os.path.join(env.root, 'resources') if (os.path.exists(path)): os.remove(path) if (os.path.isdir('resources')): shutil.rmtree('resources') shutil.copytree(resources, 'resources') vmaccess._reset_sshd_config(path) self.assertTrue(os.path.exists(path)) config = waagent.GetFileContents(path) self.assertFalse(config.startswith("#Default sshd_config")) os.remove(path)
def test_disable(self): print 'test_disable' with self.assertRaises(SystemExit) as cm: disable() self.assertEqual(cm.exception.code, 0) self.assertEqual(get_status("Disable"), 'success') download_cmd = 'python test_handler.py -download' patch_cmd = 'python test_handler.py -patch' crontab_content = waagent.GetFileContents('/etc/crontab') self.assertTrue(download_cmd not in crontab_content) self.assertTrue(patch_cmd not in crontab_content)
def get_pkg_to_patch(self, category): if not os.path.isfile(self.package_downloaded_path): return [] pkg_to_patch = waagent.GetFileContents(self.package_downloaded_path) if not pkg_to_patch: return [] patchlist = [ line.split()[0] for line in pkg_to_patch.split('\n') if line.endswith(category) ] if patchlist is None: return [] return patchlist