Example #1
0
    def test_valid_eox_call_with_validated_date_format(self):
        test_product = "WS-C3560C-8PC-S"

        sample_record = """{
            "EndOfSvcAttachDate": {
                "dateFormat": "YYYY-MM-DD",
                "value": "2017-10-30"
            },
            "EndOfServiceContractRenewal": {
                "dateFormat": "YYYY-MM-DD",
                "value": "2021-01-28"
            },
            "ProductBulletinNumber": "EOL10691",
            "EOLProductID": "WS-C3560C-8PC-S",
            "LinkToProductBulletinURL": "http://www.cisco.com/c/en/us/products/collateral/switches/catalyst-3560-c-series-switches/eos-eol-notice-c51-736180.html",
            "EOXExternalAnnouncementDate": {
                "dateFormat": "YYYY-MM-DD",
                "value": "2015-10-31"
            },
            "UpdatedTimeStamp": {
                "dateFormat": "YYYY-MM-DD",
                "value": "2015-11-03"
            },
            "EOXMigrationDetails": {
                "MigrationProductInfoURL": "http://www.cisco.com/c/en/us/products/switches/catalyst-3560-cx-series-switches/index.html",
                "PIDActiveFlag": "Y  ",
                "MigrationStrategy": " ",
                "MigrationProductName": " ",
                "MigrationProductId": "WS-C3560CX-8PC-S",
                "MigrationOption": "Enter PID(s)",
                "MigrationInformation": "Cisco Catalyst 3560-CX 8 Port PoE IP Base"
            },
            "EndOfRoutineFailureAnalysisDate": {
                "dateFormat": "YYYY-MM-DD",
                "value": "2017-10-30"
            },
            "EOXInputType": "ShowEOXByPids",
            "EndOfSaleDate": {
                "dateFormat": "YYYY-MM-DD",
                "value": "2016-10-30"
            },
            "EndOfSWMaintenanceReleases": {
                "dateFormat": "YYYY-MM-DD",
                "value": "2017-10-30"
            },
            "EOXInputValue": "WS-C3560C-8PC-S ",
            "LastDateOfSupport": {
                "dateFormat": "YYYY-MM-DD",
                "value": "2021-10-31"
            },
            "ProductIDDescription": "Catalyst 3560C Switch 8 FE PoE, 2 x Dual Uplink, IP Base"
        }"""

        eox_db_json = json.loads(sample_record)
        api_crawler.update_local_db_based_on_record(eox_db_json, True)

        p = Product.objects.get(product_id=test_product)

        self.assertEqual(p.end_of_support_date.strftime("%Y-%m-%d"), "2021-10-31")
Example #2
0
    def test_eox_update_call_with_sample_data(self):
        app_config = AppSettings()
        app_config.read_file()

        app_config.set_cisco_api_enabled(True)
        app_config.set_periodic_sync_enabled(True)
        app_config.write_file()

        eox_sample_response = os.path.join("app",
                                           "ciscoeox",
                                           "tests",
                                           "cisco_eox_sample_response.json")
        eox_db_json = json.loads(open(eox_sample_response).read())

        for record in eox_db_json['EOXRecord']:
            api_crawler.update_local_db_based_on_record(record, True)
Example #3
0
def update_cisco_eox_records(records):
    """
    update given database records from the Cisco EoX v5 API
    :param records:
    :return:
    """
    app_config = AppSettings()

    blacklist_raw_string = app_config.get_product_blacklist_regex()
    create_missing = app_config.is_auto_create_new_products()

    # build blacklist from configuration
    blacklist = []
    for e in [e.split(";") for e in blacklist_raw_string.splitlines()]:
        blacklist += e
    blacklist = [e for e in blacklist if e != ""]

    counter = 0
    messages = {}

    for record in records:
        blacklisted = False
        for regex in blacklist:
            try:
                if re.search(regex, record["EOLProductID"], re.I):
                    blacklisted = True
                    break

            except:
                logger.warning("invalid regular expression in blacklist: %s" % regex)

        if not blacklisted:
            try:
                message = cisco_eox_api_crawler.update_local_db_based_on_record(record, create_missing)
                if message:
                    messages[record["EOLProductID"]] = message

            except ValidationError as ex:
                logger.error("invalid data received from Cisco API, cannot save data object for "
                             "'%s' (%s)" % (record, str(ex)), exc_info=True)
        else:
            messages[record["EOLProductID"]] = " Product record ignored"

        counter += 1

    return {
        "count": counter,
        "messages": messages
    }
    def test_with_valid_new_records_and_create_missing(self):
        result = api_crawler.update_local_db_based_on_record(valid_eox_record, create_missing=True)

        assert result is None
        assert Product.objects.count() == 1, "Product is not created"

        p = Product.objects.get(product_id="WS-C2960-24T-S")

        assert p.end_of_new_service_attachment_date == datetime.date(2016, 10, 1)
        assert p.end_of_sw_maintenance_date == datetime.date(2016, 10, 2)
        assert p.eox_update_time_stamp == datetime.date(2016, 10, 3)
        assert p.end_of_service_contract_renewal == datetime.date(2016, 10, 4)
        assert p.end_of_sale_date == datetime.date(2016, 10, 5)
        assert p.end_of_routine_failure_analysis == datetime.date(2016, 10, 6)
        assert p.eol_ext_announcement_date == datetime.date(2016, 10, 7)
        assert p.end_of_support_date == datetime.date(2016, 10, 8)
        assert p.eol_reference_number == "12345"
        assert p.eol_reference_url == "http://www.cisco.com/en/US/products/hw/switches/ps628/prod_eol_notice0" \
                                      "900aecd804658c9.html"
    def test_migration_options_from_update_local_db_based_on_eox_record(self):
        mixer.blend("productdb.ProductGroup", name="Catalyst 2960")
        assert Product.objects.count() == 0
        assert ProductMigrationSource.objects.filter(
            name="Cisco EoX Migration option").count() == 0

        # load eox_response with test migration data
        with open(
                "app/ciscoeox/tests/data/cisco_eox_reponse_migration_data.json"
        ) as f:
            eox_records = json.loads(f.read())

        # test with valid migration option
        result = api_crawler.update_local_db_based_on_record(
            eox_records["EOXRecord"][0], create_missing=True)
        assert result is None
        assert ProductMigrationSource.objects.filter(
            name="Cisco EoX Migration option").count() == 1

        p = Product.objects.get(product_id="WS-C2950T-48-SI-WS",
                                vendor=Vendor.objects.get(id=1))

        assert p.has_migration_options() is True
        assert p.get_product_migration_source_names_set() == [
            "Cisco EoX Migration option"
        ]

        pmo = p.get_preferred_replacement_option()
        assert type(pmo) == ProductMigrationOption
        assert pmo.replacement_product_id == "WS-C2960G-48TC-L"
        assert pmo.comment == ""
        assert pmo.migration_product_info_url == ""
        assert pmo.is_replacement_in_db() is False
        assert pmo.is_valid_replacement() is True
        assert pmo.get_valid_replacement_product() is None

        # create product in database (state of the PMO should change)
        rep = mixer.blend("productdb.Product",
                          product_id="WS-C2960G-48TC-L",
                          vendor=Vendor.objects.get(id=1))
        pmo.refresh_from_db()
        assert pmo.is_replacement_in_db() is True
        assert pmo.get_valid_replacement_product().product_id == rep.product_id

        # test with missing replacement product
        result = api_crawler.update_local_db_based_on_record(
            eox_records["EOXRecord"][1], create_missing=True)
        assert result is None
        assert ProductMigrationSource.objects.filter(
            name="Cisco EoX Migration option").count() == 1

        p = Product.objects.get(product_id="WS-C2950G-24-EI",
                                vendor=Vendor.objects.get(id=1))

        assert p.has_migration_options() is True
        assert p.get_product_migration_source_names_set() == [
            "Cisco EoX Migration option"
        ]

        pmo = p.get_preferred_replacement_option()
        assert type(pmo) == ProductMigrationOption
        assert pmo.replacement_product_id == ""
        assert pmo.comment == "No Replacement Available"
        assert pmo.migration_product_info_url == "http://www.cisco.com/en/US/products/ps10538/index.html"
        assert pmo.is_replacement_in_db() is False
        assert pmo.is_valid_replacement() is False
        assert pmo.get_valid_replacement_product() is None

        # test with custom migration (no direct link to a product)
        result = api_crawler.update_local_db_based_on_record(
            eox_records["EOXRecord"][2], create_missing=True)
        assert result is None
        assert ProductMigrationSource.objects.filter(
            name="Cisco EoX Migration option").count() == 1

        p = Product.objects.get(product_id="WS-C2950G-48-EI-WS",
                                vendor=Vendor.objects.get(id=1))

        assert p.has_migration_options() is True
        assert p.get_product_migration_source_names_set() == [
            "Cisco EoX Migration option"
        ]

        pmo = p.get_preferred_replacement_option()
        assert type(pmo) == ProductMigrationOption
        assert pmo.replacement_product_id == ""
        assert pmo.comment == "Customers are encouraged to migrate to the Cisco 8540 Wireless Controller. Information " \
                              "about this product can be found at http://www.cisco.com/c/en/us/products/wireless/8540-" \
                              "wireless-controller/index.html."
        assert pmo.migration_product_info_url == "http://www.cisco.com/c/en/us/products/wireless/8540-wireless-" \
                                                 "controller/index.html"
        assert pmo.is_replacement_in_db() is False
        assert pmo.is_valid_replacement() is False
        assert pmo.get_valid_replacement_product() is None
    def test_update_local_db_based_on_record(self):
        mixer.blend("productdb.ProductGroup", name="Catalyst 2960")
        assert Product.objects.count() == 0

        # test call with invalid data
        with pytest.raises(KeyError):
            api_crawler.update_local_db_based_on_record({})

        # test call with valid data and create flag
        result = api_crawler.update_local_db_based_on_record(
            valid_eox_record, create_missing=True)

        assert result is None
        assert Product.objects.count() == 1, "The product should be created"

        # test call with valid data that already exist in the database
        result = api_crawler.update_local_db_based_on_record(
            valid_eox_record, create_missing=True)

        assert result is None
        assert Product.objects.count() == 1, "The product should be created"

        p = Product.objects.get(product_id="WS-C2960-24T-S")
        assert p.description == "Some description of the product"
        assert p.end_of_new_service_attachment_date == datetime.date(
            2016, 10, 1)
        assert p.end_of_sw_maintenance_date == datetime.date(2016, 10, 2)
        assert p.eox_update_time_stamp == datetime.date(2016, 10, 3)
        assert p.end_of_service_contract_renewal == datetime.date(2016, 10, 4)
        assert p.end_of_sale_date == datetime.date(2016, 10, 5)
        assert p.end_of_routine_failure_analysis == datetime.date(2016, 10, 6)
        assert p.eol_ext_announcement_date == datetime.date(2016, 10, 7)
        assert p.end_of_support_date == datetime.date(2016, 10, 8)
        assert p.eol_reference_number == "12345"
        assert p.eol_reference_url == "http://www.cisco.com/en/US/products/hw/switches/ps628/prod_eol_notice0" \
                                      "900aecd804658c9.html"

        # test call with valid data (updated)
        p = Product.objects.get(product_id="WS-C2960-24T-S")
        p.eox_update_time_stamp = datetime.datetime(1999, 1, 1)
        p.save()

        result = api_crawler.update_local_db_based_on_record(
            valid_eox_record, create_missing=True)

        assert result is None
        assert Product.objects.count() == 1, "The product was only updated"

        p = Product.objects.get(product_id="WS-C2960-24T-S")
        assert p.eox_update_time_stamp == datetime.date(
            2016, 10, 3), "update must be processed"
        assert p.end_of_service_contract_renewal == datetime.date(
            2016, 10, 4), "Should be the value prior the update"

        # test record with invalid URL
        invalid_record = deepcopy(valid_eox_record)
        invalid_record["LinkToProductBulletinURL"] = "Not yet provided"
        invalid_record["EOLProductID"] = "xyz"

        result = api_crawler.update_local_db_based_on_record(
            invalid_record, create_missing=True)

        assert result == "Product Data update failed: invalid EoL reference URL"
        assert Product.objects.count(
        ) == 1, "The transaction should rollback to avoid inconsistent entries in the DB"

        p = Product.objects.get(product_id="WS-C2960-24T-S")
        assert p.end_of_service_contract_renewal == datetime.date(
            2016, 10, 4), "Should be the value prior the update"

        # test empty/None values in update (should be ignored)
        p = Product.objects.get(product_id="WS-C2960-24T-S")
        p.eox_update_time_stamp = datetime.datetime(
            1999, 1, 1)  # reset the eox timestamp to trigger the update
        p.save()

        invalid_record = deepcopy(valid_eox_record)
        invalid_record["EndOfSWMaintenanceReleases"]["value"] = None
        invalid_record["EndOfRoutineFailureAnalysisDate"]["value"] = ""
        invalid_record["EndOfServiceContractRenewal"][
            "value"] = "2016-10-30"  # value in db at this time: "2016-10-04"

        result = api_crawler.update_local_db_based_on_record(
            invalid_record, create_missing=True)

        assert result is None, "the invalid data within the update should be ignored (no message is thrown)"

        # verify that the valid values are updated
        p = Product.objects.get(product_id="WS-C2960-24T-S")
        assert p.eox_update_time_stamp == datetime.date(2016, 10, 3), \
            "should be updated to the value provided by the API"
        assert p.end_of_routine_failure_analysis == datetime.date(2016, 10, 6), \
            "Should be the same value as before"
        assert p.end_of_service_contract_renewal == datetime.date(2016, 10, 30), \
            "Should be the same value as before"
    def test_with_valid_new_records(self):
        result = api_crawler.update_local_db_based_on_record(valid_eox_record)

        assert result is None
        assert Product.objects.count(
        ) == 0, "No product was created, because the created flag was not set"
Example #8
0
def update_local_database_records(results, year, records):
    for record in records:
        cisco_eox_api_crawler.update_local_db_based_on_record(record, True)

    results[str(year)] = "success"
    return results
Example #9
0
    def test_eox_update_call_with_special_character(self):
        """
        test, that no issue exists when the '%' sign is present in the ProductID
        :return:
        """
        app_config = AppSettings()
        app_config.read_file()

        app_config.set_cisco_api_enabled(True)
        app_config.set_periodic_sync_enabled(True)
        app_config.write_file()

        eox_db_record = """{
            "EndOfServiceContractRenewal": {
                "value": " ",
                "dateFormat": "YYYY-MM-DD"
            },
            "ProductIDDescription": "^IPX 8 CDP W/E1EC TO UNIVERSAL CDP (IPX 8/16/32)",
            "ProductBulletinNumber": "LEGACY_ESC_IPX_4",
            "LastDateOfSupport": {
                "value": "2003-07-01",
                "dateFormat": "YYYY-MM-DD"
            },
            "EOXInputValue": "SPA* ",
            "EOLProductID": "SPARE%",
            "UpdatedTimeStamp": {
                "value": "2015-08-23",
                "dateFormat": "YYYY-MM-DD"
            },
            "EOXInputType": "ShowEOXByPids",
            "EndOfRoutineFailureAnalysisDate": {
                "value": " ",
                "dateFormat": "YYYY-MM-DD"
            },
            "LinkToProductBulletinURL": "http://www.cisco.com/en/US/products/hw/tsd_products_support_end-of-sale_and_end-of-life_products_list.html",
            "EndOfSvcAttachDate": {
                "value": " ",
                "dateFormat": "YYYY-MM-DD"
            },
            "EndOfSaleDate": {
                "value": "1998-07-02",
                "dateFormat": "YYYY-MM-DD"
            },
            "EndOfSWMaintenanceReleases": {
                "value": " ",
                "dateFormat": "YYYY-MM-DD"
            },
            "EOXExternalAnnouncementDate": {
                "value": "1998-01-03",
                "dateFormat": "YYYY-MM-DD"
            },
            "EOXMigrationDetails": {
                "MigrationStrategy": " ",
                "MigrationProductId": " ",
                "MigrationProductInfoURL": " ",
                "PIDActiveFlag": "Y  ",
                "MigrationProductName": "See Product Bulletin",
                "MigrationInformation": " ",
                "MigrationOption": "Enter Product Name(s)"
            }
        }"""
        eox_db_json = json.loads(eox_db_record)
        api_crawler.update_local_db_based_on_record(eox_db_json, True)

        p = Product.objects.get(product_id="SPARE%")
Example #10
0
def execute_task_to_synchronize_cisco_eox_states(self, ignore_periodic_sync_flag=False):
    """
    This task synchronize the local database with the Cisco EoX API. It executes all configured queries and stores the
    results in the local database. There are two types of operation:
      * cisco_eox_api_auto_sync_auto_create_elements is set to true - will create any element which is not part of the
                                                                      blacklist and not in the database
      * cisco_eox_api_auto_sync_auto_create_elements is set to false - will only update entries, which are already
                                                                       included in the database

    :return:
    """
    app_config = AppSettings()
    run_task = app_config.is_periodic_sync_enabled()

    if run_task or ignore_periodic_sync_flag:
        logger.info("start sync with Cisco EoX API...")
        self.update_state(state=TaskState.PROCESSING, meta={
            "status_message": "sync with Cisco EoX API..."
        })

        # read configuration for the Cisco EoX API synchronization
        queries = app_config.get_cisco_eox_api_queries_as_list()
        blacklist_raw_string = app_config.get_product_blacklist_regex()
        create_missing = app_config.is_auto_create_new_products()

        if len(queries) == 0:
            result = {
                "status_message": "No Cisco EoX API queries configured."
            }

            NotificationMessage.objects.create(
                title=NOTIFICATION_MESSAGE_TITLE,
                type=NotificationMessage.MESSAGE_WARNING,
                summary_message="There are no Cisco EoX API queries configured. Nothing to do.",
                detailed_message="There are no Cisco EoX API queries configured. Please configure at least on EoX API "
                                 "query in the settings or disable the periodic synchronization."
            )

        # update the local database with the Cisco EoX API
        else:
            # test Cisco EoX API access
            test_result = utils.check_cisco_eox_api_access(
                app_config.get_cisco_api_client_id(),
                app_config.get_cisco_api_client_secret(),
                False
            )

            if test_result:
                # execute all queries from the configuration
                query_eox_records = {}
                failed_queries = []
                failed_query_msgs = {}
                successful_queries = []
                counter = 1
                for query in queries:
                    self.update_state(state=TaskState.PROCESSING, meta={
                        "status_message": "send query <code>%s</code> to the Cisco EoX API (<strong>%d of "
                                          "%d</strong>)..." % (query, counter, len(queries))
                    })

                    # wait some time between the query calls
                    time.sleep(int(app_config.get_cisco_eox_api_sync_wait_time()))
                    try:
                        query_eox_records[query] = cisco_eox_api_crawler.get_raw_api_data(api_query=query)
                        successful_queries.append(query)

                    except CiscoApiCallFailed as ex:
                        msg = "Cisco EoX API call failed (%s)" % str(ex)
                        logger.error("Query %s to Cisco EoX API failed (%s)" % (query, msg), exc_info=True)
                        failed_queries.append(query)
                        failed_query_msgs[query] = str(ex)

                    except Exception as ex:
                        msg = "Unexpected Exception, cannot access the Cisco API. Please ensure that the server is " \
                              "connected to the internet and that the authentication settings are " \
                              "valid."
                        logger.error("Query %s to Cisco EoX API failed (%s)" % (query, msg), exc_info=True)
                        failed_queries.append(query)
                        failed_query_msgs[query] = str(ex)

                    counter += 1

                # build blacklist from configuration
                blacklist = []
                for e in [e.split(";") for e in blacklist_raw_string.splitlines()]:
                    blacklist += e
                blacklist = [e for e in blacklist if e != ""]

                # update data in database
                self.update_state(state=TaskState.PROCESSING, meta={
                    "status_message": "update database..."
                })
                messages = {}
                for key in query_eox_records:
                    amount_of_records = len(query_eox_records[key])
                    self.update_state(state=TaskState.PROCESSING, meta={
                        "status_message": "update database (query <code>%s</code>, processed <b>0</b> of "
                                          "<b>%d</b> results)..." % (key, amount_of_records)
                    })
                    counter = 0
                    for record in query_eox_records[key]:
                        if counter % 100 == 0:
                            self.update_state(state=TaskState.PROCESSING, meta={
                                "status_message": "update database (query <code>%s</code>, processed <b>%d</b> of "
                                                  "<b>%d</b> results)..." % (key, counter, amount_of_records)
                            })

                        blacklisted = False
                        for regex in blacklist:
                            try:
                                if re.search(regex, record["EOLProductID"], re.I):
                                    blacklisted = True
                                    break

                            except:
                                logger.warning("invalid regular expression in blacklist: %s" % regex)

                        if not blacklisted:
                            try:
                                message = cisco_eox_api_crawler.update_local_db_based_on_record(record, create_missing)
                                if message:
                                    messages[record["EOLProductID"]] = message

                            except ValidationError as ex:
                                logger.error("invalid data received from Cisco API, cannot save data object for "
                                             "'%s' (%s)" % (record, str(ex)), exc_info=True)
                        else:
                            messages[record["EOLProductID"]] = " Product record ignored"

                        counter += 1

                # view the queries in the detailed message and all messages (if there are some)
                detailed_message = "The following queries were executed:<br><ul style=\"text-align: left;\">"
                for fq in failed_queries:
                    detailed_message += "<li class=\"text-danger\"><code>%s</code> " \
                                        "(failed, %s)</li>" % (fq, failed_query_msgs.get(fq, "unknown"))
                for sq in successful_queries:
                    detailed_message += "<li><code>%s</code> (<b>affects %d products</b>, " \
                                        "success)</li>" % (sq, len(query_eox_records[sq]))
                detailed_message += "</ul>"

                if len(messages) > 0:
                    detailed_message += "<br>The following comment/errors occurred " \
                                        "during the synchronization:<br><ul style=\"text-align: left;\">"
                    for e in messages.keys():
                        detailed_message += "<li><code>%s</code>: %s</li>" % (e, messages[e])
                    detailed_message += "</ul>"

                # show the executed queries in the summary message
                if len(failed_queries) == 0 and len(successful_queries) != 0:
                    summary_html = "The following queries were successful executed: %s" % ", ".join(
                        ["<code>%s</code>" % query for query in successful_queries]
                    )
                    NotificationMessage.objects.create(
                        title=NOTIFICATION_MESSAGE_TITLE, type=NotificationMessage.MESSAGE_SUCCESS,
                        summary_message="The synchronization with the Cisco EoX API was successful. " + summary_html,
                        detailed_message=detailed_message
                    )

                elif len(failed_queries) != 0 and len(successful_queries) == 0:
                    summary_html = "The following queries failed to execute: %s" % ", ".join(
                        ["<code>%s</code>" % query for query in failed_queries]
                    )
                    NotificationMessage.objects.create(
                        title=NOTIFICATION_MESSAGE_TITLE, type=NotificationMessage.MESSAGE_ERROR,
                        summary_message="The synchronization with the Cisco EoX API was not successful. " + summary_html,
                        detailed_message=detailed_message
                    )

                else:
                    summary_html = "The following queries were successful executed: %s\n<br>The following queries " \
                                   "failed to execute: %s" % (
                                       ", ".join(["<code>%s</code>" % query for query in successful_queries]),
                                       ", ".join(["<code>%s</code>" % query for query in failed_queries])
                                   )
                    NotificationMessage.objects.create(
                        title=NOTIFICATION_MESSAGE_TITLE, type=NotificationMessage.MESSAGE_WARNING,
                        summary_message="The synchronization with the Cisco EoX API was partially "
                                        "successful. " + summary_html,
                        detailed_message=detailed_message
                    )

                result = {"status_message": "<p style=\"text-align: left;\">" + detailed_message + "</p>"}

                # if the task was executed eager, set state to SUCCESS (required for testing)
                if self.request.is_eager:
                    self.update_state(state=TaskState.SUCCESS, meta={"status_message": summary_html})

            else:
                msg = "Cannot contact Cisco EoX API, please verify your internet connection and access " \
                      "credentials."
                if not ignore_periodic_sync_flag:
                    NotificationMessage.objects.create(
                        title=NOTIFICATION_MESSAGE_TITLE, type=NotificationMessage.MESSAGE_ERROR,
                        summary_message="The synchronization with the Cisco EoX API was not successful.",
                        detailed_message=msg
                    )

                result = {
                    "error_message": msg
                }

    else:
        result = {
            "status_message": "task not enabled"
        }

    # remove in progress flag with the cache
    cache.delete("CISCO_EOX_API_SYN_IN_PROGRESS")

    return result
def test_migration_options_from_update_local_db_based_on_eox_record():
    mixer.blend("productdb.ProductGroup", name="Catalyst 2960")
    assert Product.objects.count() == 0
    assert ProductMigrationSource.objects.filter(name="Cisco EoX Migration option").count() == 0

    # load eox_response with test migration data
    with open("app/ciscoeox/tests/data/cisco_eox_reponse_migration_data.json") as f:
        eox_records = json.loads(f.read())

    # test with valid migration option
    result = api_crawler.update_local_db_based_on_record(eox_records["EOXRecord"][0], create_missing=True)
    assert result["created"] is True
    assert result["updated"] is True
    assert ProductMigrationSource.objects.filter(name="Cisco EoX Migration option").count() == 1

    p = Product.objects.get(product_id="WS-C2950T-48-SI-WS", vendor=Vendor.objects.get(id=1))

    assert p.has_migration_options() is True
    assert p.get_product_migration_source_names_set() == ["Cisco EoX Migration option"]

    pmo = p.get_preferred_replacement_option()
    assert type(pmo) == ProductMigrationOption
    assert pmo.replacement_product_id == "WS-C2960G-48TC-L"
    assert pmo.comment == ""
    assert pmo.migration_product_info_url == ""
    assert pmo.is_replacement_in_db() is False
    assert pmo.is_valid_replacement() is True
    assert pmo.get_valid_replacement_product() is None

    # create product in database (state of the PMO should change)
    rep = mixer.blend("productdb.Product", product_id="WS-C2960G-48TC-L", vendor=Vendor.objects.get(id=1))
    pmo.refresh_from_db()
    assert pmo.is_replacement_in_db() is True
    assert pmo.get_valid_replacement_product().product_id == rep.product_id

    # test with missing replacement product
    result = api_crawler.update_local_db_based_on_record(eox_records["EOXRecord"][1], create_missing=True)
    assert result["created"] is True
    assert result["updated"] is True
    assert ProductMigrationSource.objects.filter(name="Cisco EoX Migration option").count() == 1

    p = Product.objects.get(product_id="WS-C2950G-24-EI", vendor=Vendor.objects.get(id=1))

    assert p.has_migration_options() is True
    assert p.get_product_migration_source_names_set() == ["Cisco EoX Migration option"]

    pmo = p.get_preferred_replacement_option()
    assert type(pmo) == ProductMigrationOption
    assert pmo.replacement_product_id == ""
    assert pmo.comment == "No Replacement Available"
    assert pmo.migration_product_info_url == "http://www.cisco.com/en/US/products/ps10538/index.html"
    assert pmo.is_replacement_in_db() is False
    assert pmo.is_valid_replacement() is False
    assert pmo.get_valid_replacement_product() is None

    # test with custom migration (no direct link to a product)
    result = api_crawler.update_local_db_based_on_record(eox_records["EOXRecord"][2], create_missing=True)
    assert result["created"] is True
    assert result["updated"] is True
    assert ProductMigrationSource.objects.filter(name="Cisco EoX Migration option").count() == 1

    p = Product.objects.get(product_id="WS-C2950G-48-EI-WS", vendor=Vendor.objects.get(id=1))

    assert p.has_migration_options() is True
    assert p.get_product_migration_source_names_set() == ["Cisco EoX Migration option"]

    pmo = p.get_preferred_replacement_option()
    assert type(pmo) == ProductMigrationOption
    assert pmo.replacement_product_id == ""
    assert pmo.comment == "Customers are encouraged to migrate to the Cisco 8540 Wireless Controller. Information " \
                          "about this product can be found at http://www.cisco.com/c/en/us/products/wireless/8540-" \
                          "wireless-controller/index.html."
    assert pmo.migration_product_info_url == "http://www.cisco.com/c/en/us/products/wireless/8540-wireless-" \
                                             "controller/index.html"
    assert pmo.is_replacement_in_db() is False
    assert pmo.is_valid_replacement() is False
    assert pmo.get_valid_replacement_product() is None
Example #12
0
def test_update_local_db_based_on_record():
    mixer.blend("productdb.ProductGroup", name="Catalyst 2960")
    assert Product.objects.count() == 0

    # test call with invalid data
    with pytest.raises(KeyError):
        api_crawler.update_local_db_based_on_record({})

    # test call with valid data
    result = api_crawler.update_local_db_based_on_record(valid_eox_record)

    assert "PID" in result
    assert result["PID"] == "WS-C2960-24T-S"
    assert "blacklist" in result
    assert result["blacklist"] is False
    assert "created" in result
    assert result["created"] is False
    assert "updated" in result
    assert result["updated"] is False
    assert "message" in result
    assert result["message"] is None
    assert Product.objects.count() == 0, "No product was created, because the created flag was not set"

    # test call with valid data and create flag
    result = api_crawler.update_local_db_based_on_record(valid_eox_record, create_missing=True)

    assert "PID" in result
    assert result["PID"] == "WS-C2960-24T-S"
    assert "blacklist" in result
    assert result["blacklist"] is False
    assert "created" in result
    assert result["created"] is True
    assert "updated" in result
    assert result["updated"] is True
    assert "message" in result
    assert result["message"] is None
    assert Product.objects.count() == 1, "The product should be created"

    # test call with valid data that already exist in the database
    result = api_crawler.update_local_db_based_on_record(valid_eox_record, create_missing=True)

    assert "PID" in result
    assert result["PID"] == "WS-C2960-24T-S"
    assert "blacklist" in result
    assert result["blacklist"] is False
    assert "created" in result
    assert result["created"] is False
    assert "updated" in result
    assert result["updated"] is False
    assert "message" in result
    assert "update suppressed" in result["message"]
    assert Product.objects.count() == 1, "The product should be created"

    p = Product.objects.get(product_id="WS-C2960-24T-S")
    assert p.description == "Some description of the product"
    assert p.end_of_new_service_attachment_date == datetime.date(2016, 10, 1)
    assert p.end_of_sw_maintenance_date == datetime.date(2016, 10, 2)
    assert p.eox_update_time_stamp == datetime.date(2016, 10, 3)
    assert p.end_of_service_contract_renewal == datetime.date(2016, 10, 4)
    assert p.end_of_sale_date == datetime.date(2016, 10, 5)
    assert p.end_of_routine_failure_analysis == datetime.date(2016, 10, 6)
    assert p.eol_ext_announcement_date == datetime.date(2016, 10, 7)
    assert p.end_of_support_date == datetime.date(2016, 10, 8)
    assert p.eol_reference_number == "12345"
    assert p.eol_reference_url == "http://www.cisco.com/en/US/products/hw/switches/ps628/prod_eol_notice0" \
                                  "900aecd804658c9.html"

    # test call with valid data (updated)
    p = Product.objects.get(product_id="WS-C2960-24T-S")
    p.eox_update_time_stamp = datetime.datetime(1999, 1, 1)
    p.save()

    result = api_crawler.update_local_db_based_on_record(valid_eox_record, create_missing=True)

    assert "PID" in result
    assert result["PID"] == "WS-C2960-24T-S"
    assert "blacklist" in result
    assert result["blacklist"] is False
    assert "created" in result
    assert result["created"] is False
    assert "updated" in result
    assert result["updated"] is True
    assert "message" in result
    assert result["message"] is None
    assert Product.objects.count() == 1, "The product was only updated"

    p = Product.objects.get(product_id="WS-C2960-24T-S")
    assert p.end_of_service_contract_renewal == datetime.date(2016, 10, 4), "Should be the value prior the update"

    # test crash of the update method during update
    p = Product.objects.get(product_id="WS-C2960-24T-S")
    p.eox_update_time_stamp = datetime.datetime(1999, 1, 1)  # reset the eox timestamp to trigger the update
    p.save()

    invalid_record = valid_eox_record.copy()
    invalid_record["EndOfSWMaintenanceReleases"]["value"] = None
    invalid_record["EndOfServiceContractRenewal"]["value"] = "2016-10-30"  # value in db at this time: "2016-10-04"

    result = api_crawler.update_local_db_based_on_record(invalid_record, create_missing=True)

    assert "PID" in result
    assert result["PID"] == "WS-C2960-24T-S"
    assert "blacklist" in result
    assert result["blacklist"] is False
    assert "created" in result
    assert result["created"] is False
    assert "updated" in result
    assert result["updated"] is True
    assert "message" in result
    assert "Update failed: " in result["message"]
    assert Product.objects.count() == 1, "The transaction should rollback to avoid inconsistent entries in the DB"

    # verify that the change were not saved
    p = Product.objects.get(product_id="WS-C2960-24T-S")
    assert p.eox_update_time_stamp == datetime.date(1999, 1, 1), "Should be the value prior the update"
    assert p.end_of_service_contract_renewal == datetime.date(2016, 10, 4), "Should be the value prior the update"

    # test crash of the update method during create
    invalid_record = valid_eox_record.copy()
    invalid_record["EOLProductID"] = "MyTest123"
    invalid_record["EndOfRoutineFailureAnalysisDate"]["value"] = None

    result = api_crawler.update_local_db_based_on_record(invalid_record, create_missing=True)

    assert "PID" in result
    assert result["PID"] == "MyTest123"
    assert "blacklist" in result
    assert result["blacklist"] is False
    assert "created" in result
    assert result["created"] is True
    assert "updated" in result
    assert result["updated"] is True
    assert "message" in result
    assert "Update failed: " in result["message"]
    assert Product.objects.count() == 1, "The transaction should rollback to avoid inconsistent entries in the DB"

    p = Product.objects.get(product_id="WS-C2960-24T-S")
    assert p.end_of_service_contract_renewal == datetime.date(2016, 10, 4), "Should be the value prior the update"

    # test record with invalid URL
    invalid_record = valid_eox_record.copy()
    invalid_record["LinkToProductBulletinURL"] = "Not yet provided"
    invalid_record["EOLProductID"] = "xyz"

    result = api_crawler.update_local_db_based_on_record(invalid_record, create_missing=True)

    assert "PID" in result
    assert result["PID"] == "xyz"
    assert "blacklist" in result
    assert result["blacklist"] is False
    assert "created" in result
    assert result["created"] is True
    assert "updated" in result
    assert result["updated"] is True
    assert "message" in result
    assert "Update failed: " in result["message"]
    assert Product.objects.count() == 1, "The transaction should rollback to avoid inconsistent entries in the DB"

    p = Product.objects.get(product_id="WS-C2960-24T-S")
    assert p.end_of_service_contract_renewal == datetime.date(2016, 10, 4), "Should be the value prior the update"