def main(): """ The main function. It creates VmaaS application, servers, run everything.""" vmaas_app = Application() server = tornado.httpserver.HTTPServer(vmaas_app) server.bind(PUBLIC_API_PORT) num_servers = int(os.getenv("MAX_VMAAS_SERVERS", MAX_SERVERS)) server.start(num_servers) # start forking here init_logging(num_servers) LOGGER.info("Starting (version %s).", VMAAS_VERSION) # The rest stuff must be done only after forking BaseHandler.db_cache = Cache() BaseHandler.updates_api = UpdatesAPI(BaseHandler.db_cache) BaseHandler.repo_api = RepoAPI(BaseHandler.db_cache) BaseHandler.cve_api = CveAPI(BaseHandler.db_cache) BaseHandler.errata_api = ErrataAPI(BaseHandler.db_cache) BaseHandler.dbchange_api = DBChange(BaseHandler.db_cache) vmaas_app.websocket_reconnect() vmaas_app.reconnect_callback = PeriodicCallback( vmaas_app.websocket_reconnect, WEBSOCKET_RECONNECT_INTERVAL * 1000) vmaas_app.reconnect_callback.start() IOLoop.instance().start()
def load_cache_to_apis(): """Reload cache in APIs.""" BaseHandler.updates_api = UpdatesAPI(BaseHandler.db_cache) BaseHandler.repo_api = RepoAPI(BaseHandler.db_cache) BaseHandler.cve_api = CveAPI(BaseHandler.db_cache) BaseHandler.errata_api = ErrataAPI(BaseHandler.db_cache) BaseHandler.packages_api = PackagesAPI(BaseHandler.db_cache) BaseHandler.vulnerabilities_api = VulnerabilitiesAPI(BaseHandler.db_cache, BaseHandler.updates_api) BaseHandler.dbchange_api = DBChange(BaseHandler.db_cache)
def __init__(self): handlers = [ (r"/api/internal/refresh/?", RefreshHandler), # GET request (r"/api/v1/updates/?", UpdatesHandler), # POST request (r"/api/v1/updates/[a-zA-Z0-9-._:]+", UpdatesHandler), # GET request with package name (r"/api/v1/cves/?", CVEHandler), (r"/api/v1/cves/[a-zA-Z0-9*-]+", CVEHandler), (r"/api/v1/repos/?", ReposHandler), (r"/api/v1/repos/[a-zA-Z0-9*-_]+", ReposHandler), (r"/api/v1/errata/?", ErrataHandler), # POST request (r"/api/v1/errata/[a-zA-Z0-9*-:]+", ErrataHandler) # GET request ] cursor = Database().cursor() self.updatesapi = UpdatesAPI(cursor) self.cveapi = CveAPI(cursor) self.repoapi = RepoAPI(cursor) self.errataapi = ErrataAPI(cursor) tornado.web.Application.__init__(self, handlers)
def setup_api(self, load_cache): """Setup UpdatesAPI object.""" self.updates_api = UpdatesAPI(self.cache)
class TestUpdatesAPI(TestBase): """Test updates api class.""" updates_api = None @pytest.fixture(autouse=True) def setup_api(self, load_cache): """Setup UpdatesAPI object.""" self.updates_api = UpdatesAPI(self.cache) def test_process_repos(self): """Test repos processing.""" response = {} repo_ids = self.updates_api._process_repositories( UPDATES_JSON, response) assert len(repo_ids) == 1 def test_process_repos_repo(self): """Test repos processing - filter out repository_list.""" response = {} repo_ids = self.updates_api._process_repositories( UPDATES_JSON_REPO, response) assert repo_ids == set() def test_process_repos_release(self): """Test repos processing - filter out releasever.""" response = {} repo_ids = self.updates_api._process_repositories( UPDATES_JSON_RELEASE, response) assert repo_ids == set() def test_process_repos_arch(self): """Test repos processing - filter out basearch.""" response = {} repo_ids = self.updates_api._process_repositories( UPDATES_JSON_ARCH, response) assert repo_ids == set() def test_process_input_pkg(self): """Test filtering out unknown (or without updates) package names.""" response = {} response["update_list"] = {} response["update_list"][PKG] = {} pkgs = self.updates_api._process_input_packages(UPDATES_JSON, response) assert pkgs def test_process_input_non_exist(self): """Test filtering out unknown non existing package.""" response = {} response["update_list"] = {} response["update_list"][PKG] = {} pkgs = self.updates_api._process_input_packages( UPDATES_JSON_NON_EXIST, response) assert pkgs == {} def test_schema_v1(self): """Test schema of updates api v1.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(1, UPDATES_JSON.copy()) assert schemas.updates_top_all_schema.validate(updates) assert schemas.updates_package_schema.validate( updates["update_list"][PKG]) def test_schema_v2(self): """Test schema of updates api v2.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(2, UPDATES_JSON.copy()) assert schemas.updates_top_all_schema.validate(updates) assert schemas.updates_package_schema_v2.validate( updates["update_list"][PKG]) def test_schema_repolist(self): """Test repolist schema of updates api v2.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(2, UPDATES_JSON_REPO.copy()) assert schemas.updates_top_repolist_schema.validate(updates) def test_schema_releasever(self): """Test releasever schema of updates api v2.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(2, UPDATES_JSON_RELEASE.copy()) assert schemas.updates_top_releasever_schema.validate(updates) def test_schema_basearch(self): """Test basearch schema of updates api v2.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(2, UPDATES_JSON_ARCH.copy()) assert schemas.updates_top_basearch_schema.validate(updates) def test_process_list_v1(self): """Test looking for package updates api v1.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(1, UPDATES_JSON.copy()) assert updates def test_process_list_v2(self): """Test looking for package updates api v2.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(2, UPDATES_JSON.copy()) assert updates def test_process_empty_pkg_list_v1(self): """Test empty package_list updates api v1.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(1, UPDATES_JSON_EMPTY_LIST.copy()) assert updates == EMPTY_RESPONSE def test_process_empty_pkg_list_v2(self): """Test empty package_list updates api v2.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(2, UPDATES_JSON_EMPTY_LIST.copy()) assert updates == EMPTY_RESPONSE def test_process_non_exist_v1(self): """Test non-existing package updates api v1.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(1, UPDATES_JSON_NON_EXIST.copy()) assert updates == NON_EXIST_RESPONSE def test_process_non_exist_v2(self): """Test non-existing package updates api v2.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(2, UPDATES_JSON_NON_EXIST.copy()) assert updates == NON_EXIST_RESPONSE
class TestModularity(TestBase): """Test /updates modularity""" updates_api = None @staticmethod def gen_pkg_json(nevra, modules=None): """Prepares a request to VMaaS""" retval = {'package_list': [nevra]} if modules is not None: retval['modules_list'] = modules return retval @pytest.fixture(autouse=True) def setup_api(self, load_cache): """Setup UpdatesAPI object.""" self.updates_api = UpdatesAPI(self.cache) @pytest.mark.parametrize('test_data', OLD_OLD, ids=[x[0] for x in OLD_OLD]) def test_old_pkg_old_module(self, test_data): """ Test with my-pkg-1.1.0-1.el8.i686 Test without modularity info provided, should find 3 updates: my-pkg-1.2.0-1.el8.i686 my-pkg-2.0.0-1.el8.i686 my-pkg-2.1.0-1.el8.i686 Test with modularity info provided, should find one update: my-pkg-1.2.0-1.el8.i686 """ pkg = PKG_NEVRAS[0] mode, modules, expected_update_pkgs = test_data # pylint:disable=unused-variable updates = self.updates_api.process_list( 2, self.gen_pkg_json(pkg, modules)) assert updates available_updates = updates['update_list'][pkg]['available_updates'] assert len(available_updates) == len(expected_update_pkgs) assert expected_update_pkgs == { rec['package'] for rec in available_updates } @pytest.mark.parametrize('test_data', NEW_OLD, ids=[x[0] for x in NEW_OLD]) def test_new_pkg_old_module(self, test_data): """Test with my-pkg-1.2.0-1.el8.i686 Test without modularity info enabled, should find two updates my-pkg-2.0.0-1.el8.i686 my-pkg-2.1.0-1.el8.i686 With correct modularity stream enabled there should be no upates Test with incorrect modularity information, should find two (incorrect) update: my-pkg-2.0.0-1.el8.i686 my-pkg-2.1.0-1.el8.i686 DNF on the client should never let this happen """ pkg = PKG_NEVRAS[1] mode, modules, expected_update_pkgs = test_data # pylint:disable=unused-variable updates = self.updates_api.process_list( 2, self.gen_pkg_json(pkg, modules)) assert updates available_updates = updates['update_list'][pkg]['available_updates'] if mode == 'correct_stream_enabled': # with correct stream enabled there should be no updates assert not available_updates return assert len(available_updates) == len(expected_update_pkgs) assert expected_update_pkgs == { rec['package'] for rec in available_updates } @pytest.mark.parametrize('test_data', NEW_MODULE, ids=[x[0] for x in NEW_MODULE]) def test_old_pkg_new_module(self, test_data): """ Test with my-pkg-2.0.0-1.el8.i686 Both tests with, or without modularity should find one update my-pkg-2.1.0-1.el8.i686 """ pkg = PKG_NEVRAS[2] mode, modules, expected_update_pkgs = test_data # pylint:disable=unused-variable updates = self.updates_api.process_list( 2, self.gen_pkg_json(pkg, modules)) assert updates available_updates = updates['update_list'][pkg]['available_updates'] assert len(available_updates) == len(expected_update_pkgs) assert expected_update_pkgs == { rec['package'] for rec in available_updates } @pytest.mark.parametrize('test_data', NEW_MODULE, ids=[x[0] for x in NEW_MODULE]) def test_new_pkg_new_module(self, test_data): """ Test with my-pkg-2.1.0-1.el8.i686 which is the latest package and has no updates There should be zero updates with or without modularity """ pkg = PKG_NEVRAS[3] mode, modules, expected_update_pkgs = test_data # pylint:disable=unused-variable updates = self.updates_api.process_list( 2, self.gen_pkg_json(pkg, modules)) assert updates assert not updates['update_list'][pkg]['available_updates'] @pytest.mark.parametrize('test_data', NO_MODULES, ids=[x[0] for x in NO_MODULES]) def test_no_enabled_modules(self, test_data): """ Tests with modularity functionaility enabled witout needed module (my-pkg 1) enabled Scenarios: no modules enabled at all, only rhn-tools module enabled, module not present in the database enabled All of these should result in no updates found """ pkg = PKG_NEVRAS[0] mode, modules = test_data # pylint:disable=unused-variable updates = self.updates_api.process_list( 2, self.gen_pkg_json(pkg, modules)) assert updates assert not updates['update_list'][pkg]['available_updates']
def setup_api(self, load_cache): """Setup UpdatesAPI object.""" self.vulnerabilities_api = VulnerabilitiesAPI(self.cache, UpdatesAPI(self.cache))
class TestUpdatesAPI(TestBase): """Test updates api class.""" updates_api = None @pytest.fixture(autouse=True) def setup_api(self, load_cache): """Setup UpdatesAPI object.""" self.updates_api = UpdatesAPI(self.cache) def test_process_input_pkg(self): """Test filtering out unknown (or without updates) package names.""" pkgs, update_list = self.updates_api._process_input_packages(UPDATES_JSON) assert pkgs == {'my-pkg-1.1.0-1.el8.i686': {'parsed_nevra': ('my-pkg', '0', '1.1.0', '1.el8', 'i686')}} assert update_list == {'my-pkg-1.1.0-1.el8.i686': {}} def test_process_input_non_exist(self): """Test filtering out unknown non existing package.""" pkgs, update_list = self.updates_api._process_input_packages(UPDATES_JSON_NON_EXIST) assert pkgs == {} assert update_list == {'non-exist': {}} def test_schema_v1(self): """Test schema of updates api v1.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(1, UPDATES_JSON.copy()) assert schemas.updates_top_all_schema.validate(updates) assert schemas.updates_package_schema.validate(updates["update_list"][PKG]) def test_schema_v2(self): """Test schema of updates api v2.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(2, UPDATES_JSON.copy()) assert schemas.updates_top_all_schema.validate(updates) assert schemas.updates_package_schema_v2.validate(updates["update_list"][PKG]) def test_schema_repolist(self): """Test repolist schema of updates api v2.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(2, UPDATES_JSON_REPO.copy()) assert schemas.updates_top_repolist_schema.validate(updates) def test_schema_releasever(self): """Test releasever schema of updates api v2.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(2, UPDATES_JSON_RELEASE.copy()) assert schemas.updates_top_releasever_schema.validate(updates) def test_schema_basearch(self): """Test basearch schema of updates api v2.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(2, UPDATES_JSON_ARCH.copy()) assert schemas.updates_top_basearch_schema.validate(updates) def test_process_list_v1(self): """Test looking for package updates api v1.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(1, UPDATES_JSON.copy()) assert updates == UPDATES_RESPONSE_1 def test_process_list_v2(self): """Test looking for package updates api v2.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(2, UPDATES_JSON.copy()) assert updates == UPDATES_RESPONSE_2 def test_process_list_v2_unknown(self): """Test looking for unknown EVRA updates, disabled optimistic_updates.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(2, UPDATES_JSON_UNKNOWN.copy()) assert updates == UPDATES_RESPONSE_2_UNKNOWN_NONE def test_optimistic_updates_1(self): """Test looking for unknown EVRA updates (module_stream: 1).""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(2, UPDATES_JSON_UNKNOWN_OPT_UPD.copy()) assert updates == UPDATES_RESPONSE_2_UNKNOWN def test_optimistic_updates_2(self): """Test looking for unknown EVRA updates (module_stream: 2).""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(2, UPDATES_JSON_UNKNOWN_OPT_UPD_2.copy()) assert updates == UPDATES_RESPONSE_2_UNKNOWN_2 def test_process_empty_pkg_list_v1(self): """Test empty package_list updates api v1.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(1, UPDATES_JSON_EMPTY_LIST.copy()) assert updates == EMPTY_RESPONSE def test_process_empty_pkg_list_v2(self): """Test empty package_list updates api v2.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(2, UPDATES_JSON_EMPTY_LIST.copy()) assert updates == EMPTY_RESPONSE def test_process_non_exist_v1(self): """Test non-existing package updates api v1.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(1, UPDATES_JSON_NON_EXIST.copy()) assert updates == NON_EXIST_RESPONSE def test_process_non_exist_v2(self): """Test non-existing package updates api v2.""" # NOTE: use copy of dict with json input, because process_list changes this dict updates = self.updates_api.process_list(2, UPDATES_JSON_NON_EXIST.copy()) assert updates == NON_EXIST_RESPONSE
class TestInternalUpdatesAPI(TestBase): """Test internal methods of updates api class.""" updates_api = None @pytest.fixture(autouse=True) def setup_api(self, load_cache): """Setup UpdatesAPI object.""" self.updates_api = UpdatesAPI(self.cache) def test_get_repository_list_1(self): """Test with complete request.""" repositories_list, repo_ids = self.updates_api._get_repository_list(UPDATES_JSON) assert repositories_list == ['rhel-7-server-rpms'] assert repo_ids == [1] def test_get_repository_list_3(self): """Test with 'repository_list' only request.""" repositories_list, repo_ids = self.updates_api._get_repository_list(UPDATES_JSON_REPO) assert repositories_list == ['rhel-6-server-rpms'] assert repo_ids == [] def test_get_repository_list_4(self): """Test with 'releasever' only request.""" repositories_list, repo_ids = self.updates_api._get_repository_list(UPDATES_JSON_RELEASE) assert repositories_list is None assert list(repo_ids) == [1, 2, 3, 4, 5, 6] def test_get_repository_list_5(self): """Test with 'basearch' only request.""" repositories_list, repo_ids = self.updates_api._get_repository_list(UPDATES_JSON_ARCH) assert repositories_list is None assert list(repo_ids) == [1, 2, 3, 4, 5, 6] def test_get_releasever_1(self): """Test with complete request""" repo_ids_in = [1] releasever, repo_ids = self.updates_api._get_releasever(UPDATES_JSON, repo_ids_in) assert releasever == '7Server' assert repo_ids == [1] def test_get_releasever_2(self): """Test with 'releasever' only request.""" repo_ids_in = [1, 2, 3, 4, 5] releasever, repo_ids = self.updates_api._get_releasever(UPDATES_JSON_RELEASE, repo_ids_in) assert releasever == '6Server' assert repo_ids == [] def test_get_releasever_3(self): """Test with 'basearch' only request.""" repo_ids_in = [1, 2, 3, 4, 5] releasever, repo_ids = self.updates_api._get_releasever(UPDATES_JSON_ARCH, repo_ids_in) assert releasever is None assert repo_ids == repo_ids_in def test_get_basearch_1(self): """Test with complete request.""" repo_ids_in = [1] basearch, repo_ids = self.updates_api._get_basearch(UPDATES_JSON, repo_ids_in) assert basearch == 'x86_64' assert repo_ids == {1} def test_get_basearch_2(self): """Test with 'repository_list' only request.""" repo_ids_in = [] basearch, repo_ids = self.updates_api._get_basearch(UPDATES_JSON_REPO, repo_ids_in) assert basearch is None assert repo_ids == set() def test_get_basearch_3(self): """Test with 'releasever' only request.""" repo_ids_in = [] basearch, repo_ids = self.updates_api._get_basearch(UPDATES_JSON_RELEASE, repo_ids_in) assert basearch is None assert repo_ids == set() def test_get_basearch_4(self): """Test with 'basearch' only request.""" repo_ids_in = [1, 2, 3, 4, 5] basearch, repo_ids = self.updates_api._get_basearch(UPDATES_JSON_ARCH, repo_ids_in) assert basearch == 'i386' assert repo_ids == set()
class TestModularity(TestBase): """Test /updates modularity""" updates_api = None @staticmethod def gen_pkg_json(nevra, modules=None): """Prepares a request to VMaaS""" retval = {'package_list': [nevra]} if modules is not None: retval['modules_list'] = modules return retval @pytest.fixture(autouse=True) def setup_api(self, load_cache): """Setup UpdatesAPI object.""" self.updates_api = UpdatesAPI(self.cache) @pytest.mark.parametrize('test_data', OLD_OLD, ids=[x[0] for x in OLD_OLD]) def test_old_pkg_old_module(self, test_data): """ Test with postgresql-9.6.10-1.el8+1547+210b7007.x86_64 Test without modularity info provided, should find 2 updates: postgresql-9.6.10-1.module+el8+2470+d1bafa0e.x86_64.rpm postgresql-10.6-1.module+el8+2469+5ecd5aae.x86_64.rpm Test with modularity info provided, should find one update: postgresql-9.6.10-1.module+el8+2470+d1bafa0e.x86_64.rpm """ updates = self.updates_api.process_list(2, self.gen_pkg_json(PKG_NEVRAS[0], test_data[1])) assert updates available_updates = updates['update_list'][PKG_NEVRAS[0]]['available_updates'] assert len(available_updates) == len(test_data[2]) assert test_data[2] == {rec['package'] for rec in available_updates} @pytest.mark.parametrize('test_data', NEW_OLD, ids=[x[0] for x in NEW_OLD]) def test_new_pkg_old_module(self, test_data): """Test with postgresql-9.6.10-1.module+el8+2470+d1bafa0e.x86_64 Test without modularity info enabled, should find one update postgresql-10.6-1.module+el8+2469+5ecd5aae.x86_64.rpm With correct modularity stream enabled there should be no upates Test with incorrect modularity information, should find one (incorrect) update: postgresql-10.6-1.module+el8+2469+5ecd5aae.x86_64 DNF on the client should never let this happen """ updates = self.updates_api.process_list(2, self.gen_pkg_json(PKG_NEVRAS[1], test_data[1])) assert updates available_updates = updates['update_list'][PKG_NEVRAS[1]]['available_updates'] if test_data[0] == 'correct_stream_enabled': # with correct stream enabled there should be no updates assert not available_updates return assert len(available_updates) == len(test_data[2]) assert test_data[2] == {rec['package'] for rec in available_updates} @pytest.mark.parametrize('test_data', NEW_MODULE, ids=[x[0] for x in NEW_MODULE]) def test_old_pkg_new_module(self, test_data): """ Test with postgresql-10.5-1.el8+1546+27ad5f8e.x86_64 Both tests with, or without modularity should find one update postgresql-10.6-1.module+el8+2469+5ecd5aae.x86_64 """ updates = self.updates_api.process_list(2, self.gen_pkg_json(PKG_NEVRAS[2], test_data[1])) assert updates available_updates = updates['update_list'][PKG_NEVRAS[2]]['available_updates'] assert len(available_updates) == len(test_data[2]) assert test_data[2] == {rec['package'] for rec in available_updates} @pytest.mark.parametrize('test_data', NEW_MODULE, ids=[x[0] for x in NEW_MODULE]) def test_new_pkg_new_module(self, test_data): """ Test with postgresql-10.6-1.module+el8+2469+5ecd5aae.x86_64 which is the latest package and has no updates There should be zero updates with or without modularity """ updates = self.updates_api.process_list(2, self.gen_pkg_json(PKG_NEVRAS[3], test_data[1])) assert updates assert not updates['update_list'][PKG_NEVRAS[3]]['available_updates'] @pytest.mark.parametrize('test_data', NO_MODULES, ids=[x[0] for x in NO_MODULES]) def test_no_enabled_modules(self, test_data): """ Tests with modularity functionaility enabled witout needed module (postgresql 9.6) enabled Scenarios: no modules enabled at all, only rhn-tools module enabled, module not present in the database enabled All of these should result in no updates found """ updates = self.updates_api.process_list(2, self.gen_pkg_json(PKG_NEVRAS[0], test_data[1])) assert updates assert not updates['update_list'][PKG_NEVRAS[0]]['available_updates']