def test_host_model_default_timestamps(db_create_host): host = Host(account=ACCOUNT, canonical_facts={"fqdn": "fqdn"}) before_commit = now() db_create_host(host) after_commit = now() assert isinstance(host.created_on, datetime) assert before_commit < host.created_on < after_commit assert isinstance(host.modified_on, datetime) assert before_commit < host.modified_on < after_commit
def test_host_model_updated_timestamp(db_create_host): host = Host(account=ACCOUNT, canonical_facts={"fqdn": "fqdn"}) before_insert_commit = now() db_create_host(host) after_insert_commit = now() host.canonical_facts = {"fqdn": "ndqf"} before_update_commit = now() db.session.commit() after_update_commit = now() assert before_insert_commit < host.created_on < after_insert_commit assert before_update_commit < host.modified_on < after_update_commit
def test_events_sent_to_correct_topic(mocker, flask_app, secondary_topic_enabled): host_id = generate_uuid() insights_id = generate_uuid() host = minimal_host(id=host_id, insights_id=insights_id) add_host = mocker.patch( "app.queue.queue.add_host", return_value=({"id": host_id}, host_id, insights_id, AddHostResult.created) ) mock_event_producer = mocker.Mock() message = wrap_message(host.data()) handle_message(json.dumps(message), mock_event_producer) # checking events sent to both egress and events topic assert mock_event_producer.write_event.call_count == 2 assert mock_event_producer.write_event.call_args_list[0][0][3] == Topic.egress assert mock_event_producer.write_event.call_args_list[1][0][3] == Topic.events mock_event_producer.reset_mock() # for host update events add_host.return_value = ({"id": host_id}, host_id, insights_id, AddHostResult.updated) message["data"].update(stale_timestamp=(now() + timedelta(hours=26)).isoformat()) handle_message(json.dumps(message), mock_event_producer) # checking events sent to both egress and events topic assert mock_event_producer.write_event.call_count == 2 assert mock_event_producer.write_event.call_args_list[0][0][3] == Topic.egress assert mock_event_producer.write_event.call_args_list[1][0][3] == Topic.events
def db_host(**values): data = { "account": USER_IDENTITY["account_number"], "display_name": "test-display-name", "ansible_host": "test-ansible-host", "canonical_facts": { "insights_id": generate_uuid(), "subscription_manager_id": generate_uuid(), "bios_uuid": generate_uuid(), "fqdn": "test-fqdn", "satellite_id": generate_uuid(), "rhel_machine_id": generate_uuid(), "ip_addresses": ["10.0.0.1"], "mac_addresses": ["aa:bb:cc:dd:ee:ff"], }, "facts": { "ns1": { "key1": "value1" } }, "tags": { "ns1": { "key1": ["val1", "val2"], "key2": ["val1"] }, "SPECIAL": { "tag": ["ToFind"] } }, "stale_timestamp": (now() + timedelta(days=randint(1, 7))).isoformat(), "reporter": "test-reporter", **values, } return Host(**data)
def test_add_host_stale_timestamp(event_datetime_mock, mq_create_or_update_host): """ Tests to see if the host is successfully created with both reporter and stale_timestamp set. """ expected_insights_id = generate_uuid() timestamp_iso = event_datetime_mock.isoformat() stale_timestamp = now() host = minimal_host(insights_id=expected_insights_id, stale_timestamp=stale_timestamp.isoformat()) expected_results = { "host": { **host.data(), "stale_warning_timestamp": (stale_timestamp + timedelta(weeks=1)).isoformat(), "culled_timestamp": (stale_timestamp + timedelta(weeks=2)).isoformat(), }, "platform_metadata": {}, "timestamp": timestamp_iso, "type": "created", } host_keys_to_check = ["reporter", "stale_timestamp", "culled_timestamp"] key, event, headers = mq_create_or_update_host(host, return_all_data=True) assert_mq_host_data(key, event, expected_results, host_keys_to_check)
def _test_order_by_id_desc(inventory_config, api_get, subtests, created_hosts, specifications, order_by, order_how): for updates, expected_added_hosts in specifications: # Update hosts to they have a same modified_on timestamp, but different IDs. # New modified_on value must be set explicitly so it’s saved the same to all # records. Otherwise SQLAlchemy would consider it unchanged and update it # automatically to its own "now" only for records whose ID changed. new_modified_on = now() for added_host_index, new_id in updates: host = update_host_in_db(created_hosts[added_host_index].id, id=new_id, modified_on=new_modified_on) created_hosts[added_host_index] = serialize_db_host(host, inventory_config) # Check the order in the response against the expected order. Only indexes # are passed, because self.added_hosts values were replaced during the # update. expected_hosts = tuple(created_hosts[added_host_index] for added_host_index in expected_added_hosts) urls = (HOST_URL, build_hosts_url(created_hosts), build_system_profile_url(created_hosts)) for url in urls: with subtests.test(url=url, updates=updates): order_query_parameters = build_order_query_parameters(order_by=order_by, order_how=order_how) response_status, response_data = api_get(url, query_parameters=order_query_parameters) assert_response_status(response_status, expected_status=200) assert_host_ids_in_response(response_data, expected_hosts)
def test_ignore_culled_host_on_update_by_elevated_id( api_create_or_update_host): # Culled host host = minimal_host(insights_id=generate_uuid(), stale_timestamp=(now() - timedelta(weeks=3)).isoformat()) # Create the host multi_response_status, multi_response_data = api_create_or_update_host( [host]) assert_response_status(multi_response_status, 207) create_host_response = get_host_from_multi_response(multi_response_data) assert_host_was_created(create_host_response) # Update the host host.ip_addresses = ["10.10.0.2"] multi_response_status, multi_response_data = api_create_or_update_host( [host]) assert_response_status(multi_response_status, 207) update_host_response = get_host_from_multi_response(multi_response_data) assert_host_was_created(update_host_response) assert create_host_response["host"]["id"] != update_host_response["host"][ "id"]
def test_ignore_culled_host_on_update_by_canonical_facts( api_create_or_update_host): # Culled host host = minimal_host(fqdn="my awesome fqdn", stale_timestamp=(now() - timedelta(weeks=3)).isoformat()) # Create the host multi_response_status, multi_response_data = api_create_or_update_host( [host]) assert_response_status(multi_response_status, 207) create_host_response = get_host_from_multi_response(multi_response_data) assert_host_was_created(create_host_response) # Update the host multi_response_status, multi_response_data = api_create_or_update_host( [host]) assert_response_status(multi_response_status, 207) update_host_response = get_host_from_multi_response(multi_response_data) assert_host_was_created(update_host_response) assert create_host_response["host"]["id"] != update_host_response["host"][ "id"]
def test_update_existing_host_fix_display_name_using_input_fqdn( db_create_host): # Create an "existing" host fqdn = "host1.domain1.com" existing_host = db_create_host(extra_data={ "canonical_facts": { "fqdn": fqdn }, "display_name": None }) # Clear the display_name existing_host.display_name = None db.session.commit() assert existing_host.display_name is None # Update the host expected_fqdn = "different.domain1.com" input_host = Host({"fqdn": expected_fqdn}, display_name="", reporter="puptoo", stale_timestamp=now()) existing_host.update(input_host) assert existing_host.display_name == expected_fqdn
def test_update_existing_host_fix_display_name_using_existing_fqdn( db_create_host): expected_fqdn = "host1.domain1.com" insights_id = generate_uuid() existing_host = db_create_host( extra_data={ "canonical_facts": { "fqdn": expected_fqdn, "insights_id": insights_id }, "display_name": None }) # Clear the display_name existing_host.display_name = None db.session.commit() assert existing_host.display_name is None # Update the host input_host = Host({"insights_id": insights_id}, display_name="", reporter="puptoo", stale_timestamp=now()) existing_host.update(input_host) assert existing_host.display_name == expected_fqdn
def test_host_model_assigned_values(db_create_host, db_get_host): values = { "account": USER_IDENTITY["account_number"], "display_name": "display_name", "ansible_host": "ansible_host", "facts": [{ "namespace": "namespace", "facts": { "key": "value" } }], "tags": { "namespace": { "key": ["value"] } }, "canonical_facts": { "fqdn": "fqdn" }, "system_profile_facts": { "number_of_cpus": 1 }, "stale_timestamp": now(), "reporter": "reporter", } inserted_host = Host(**values) db_create_host(inserted_host) selected_host = db_get_host(inserted_host.id) for key, value in values.items(): assert getattr(selected_host, key) == value
def test_host_model_default_timestamps(db_create_host): host = Host( account=USER_IDENTITY["account_number"], canonical_facts={"fqdn": "fqdn"}, reporter="yupana", stale_timestamp=now(), ) before_commit = now() db_create_host(host=host) after_commit = now() assert isinstance(host.created_on, datetime) assert before_commit < host.created_on < after_commit assert isinstance(host.modified_on, datetime) assert before_commit < host.modified_on < after_commit
def test_host_model_timestamp_timezones(db_create_host): host = Host(account=ACCOUNT, canonical_facts={"fqdn": "fqdn"}, stale_timestamp=now(), reporter="ingress") db_create_host(host) assert host.created_on.tzinfo assert host.modified_on.tzinfo assert host.stale_timestamp.tzinfo
def test_host_model_constraints(field, value, db_create_host): values = {"account": ACCOUNT, "canonical_facts": {"fqdn": "fqdn"}, **{field: value}} if field == "reporter": values["stale_timestamp"] = now() host = Host(**values) with pytest.raises(DataError): db_create_host(host)
def minimal_db_host(**values): data = { "account": ACCOUNT, "canonical_facts": {"insights_id": generate_uuid()}, "stale_timestamp": (now() + timedelta(days=randint(1, 7))).isoformat(), "reporter": "test-reporter", **values, } return Host(**data)
def test_host_model_default_id(db_create_host): host = Host( account=USER_IDENTITY["account_number"], canonical_facts={"fqdn": "fqdn"}, reporter="yupana", stale_timestamp=now(), ) db_create_host(host=host) assert isinstance(host.id, uuid.UUID)
def test_host_model_updated_timestamp(db_create_host): host = Host( account=USER_IDENTITY["account_number"], canonical_facts={"fqdn": "fqdn"}, reporter="yupana", stale_timestamp=now(), ) before_insert_commit = now() db_create_host(host=host) after_insert_commit = now() host.canonical_facts = {"fqdn": "ndqf"} before_update_commit = now() db.session.commit() after_update_commit = now() assert before_insert_commit < host.created_on < after_insert_commit assert before_update_commit < host.modified_on < after_update_commit
def test_host_schema_ignored_tags(tags): host = { "fqdn": "fred.flintstone.com", "display_name": "display_name", "account": ACCOUNT, "tags": tags, "stale_timestamp": now().isoformat(), "reporter": "test", } validated_host = HttpHostSchema(strict=True).load(host) assert "tags" not in validated_host.data
def test_host_schema_valid_tags(tags): host = { "fqdn": "fred.flintstone.com", "display_name": "display_name", "account": USER_IDENTITY["account_number"], "tags": tags, "stale_timestamp": now().isoformat(), "reporter": "test", } validated_host = HostSchema().load(host) assert validated_host["tags"] == tags
def test_host_schema_timezone_enforced(): host = { "fqdn": "scooby.doo.com", "display_name": "display_name", "account": USER_IDENTITY["account_number"], "stale_timestamp": now().replace(tzinfo=None).isoformat(), "reporter": "test", } with pytest.raises(MarshmallowValidationError) as exception: HostSchema().load(host) assert "Timestamp must contain timezone info" in str(exception.value)
def test_host_schema_timezone_enforced(schema): host = { "fqdn": "scooby.doo.com", "display_name": "display_name", "account": ACCOUNT, "stale_timestamp": now().replace(tzinfo=None).isoformat(), "reporter": "test", } with pytest.raises(ValidationError) as exception: schema(strict=True).load(host) assert "Timestamp must contain timezone info" in str(exception.value)
def test_culled_timestamp(culling_culled_offset_days, inventory_config, mq_create_or_update_host, api_get): inventory_config.culling_culled_offset_days = culling_culled_offset_days stale_timestamp = now() + timedelta(hours=1) host = minimal_host(stale_timestamp=stale_timestamp.isoformat()) created_host = mq_create_or_update_host(host) url = build_hosts_url(created_host.id) response_status, response_data = api_get(url) assert response_status == 200 culled_timestamp = stale_timestamp + timedelta(days=culling_culled_offset_days) assert culled_timestamp.isoformat() == response_data["results"][0]["culled_timestamp"]
def test_host_model_timestamp_timezones(db_create_host): host = Host( account=USER_IDENTITY["account_number"], canonical_facts={"fqdn": "fqdn"}, stale_timestamp=now(), reporter="ingress", ) db_create_host(host=host) assert host.created_on.tzinfo assert host.modified_on.tzinfo assert host.stale_timestamp.tzinfo
def minimal_db_host(**values): data = { "account": USER_IDENTITY["account_number"], "canonical_facts": { "insights_id": generate_uuid() }, "stale_timestamp": (now() + timedelta(days=randint(1, 7))), "reporter": "test-reporter", **values, } if "account" in values: data["account"] = values.get("account") return Host(**data)
def test_host_schema_invalid_tags(tags): host = { "fqdn": "fred.flintstone.com", "display_name": "display_name", "account": ACCOUNT, "tags": tags, "stale_timestamp": now().isoformat(), "reporter": "test", } with pytest.raises(ValidationError) as exception: MqHostSchema(strict=True).load(host) error_messages = exception.value.normalized_messages() assert "tags" in error_messages assert error_messages["tags"] == {0: {"key": ["Missing data for required field."]}}
def test_host_model_constraints(field, value, db_create_host): values = { "account": USER_IDENTITY["account_number"], "canonical_facts": {"fqdn": "fqdn"}, "stale_timestamp": now(), **{field: value}, } # add reporter if it's missing because it is now required all the time if not values.get("reporter"): values["reporter"] = "yupana" host = Host(**values) with pytest.raises(DataError): db_create_host(host=host)
def test_host_models_missing_fields(missing_field): limited_values = { "account": USER_IDENTITY["account_number"], "canonical_facts": {"fqdn": "foo.qoo.doo.noo"}, "system_profile_facts": {"number_of_cpus": 1}, } if missing_field in limited_values: limited_values[missing_field] = None # LimitedHost should be fine with these missing values LimitedHost(**limited_values) values = {**limited_values, "stale_timestamp": now(), "reporter": "reporter"} if missing_field in values: values[missing_field] = None # Host should complain about the missing values with pytest.raises(InventoryException): Host(**values)
def test_with_stale_timestamp(mq_create_or_update_host, api_get): stale_timestamp = now() reporter = "some reporter" host = minimal_host(fqdn="matching fqdn", stale_timestamp=stale_timestamp.isoformat(), reporter=reporter) created_host = mq_create_or_update_host(host) assert_system_culling_data(created_host.data(), stale_timestamp, reporter) updated_host = mq_create_or_update_host(host) assert_system_culling_data(updated_host.data(), stale_timestamp, reporter) response_status, response_data = api_get(HOST_URL) assert response_status == 200 assert_system_culling_data(response_data["results"][0], stale_timestamp, reporter) response_status, response_data = api_get(build_hosts_url(created_host.id)) assert response_status == 200 assert_system_culling_data(response_data["results"][0], stale_timestamp, reporter)
def test_always_update_stale_timestamp_from_next_reporter( api_create_or_update_host, db_get_host, new_stale_timestamp, new_reporter): old_stale_timestamp = now() + timedelta(days=2) old_reporter = "old reporter" host = minimal_host(stale_timestamp=old_stale_timestamp.isoformat(), reporter=old_reporter) multi_response_status, multi_response_data = api_create_or_update_host( [host]) assert_response_status(multi_response_status, 207) create_host_response = get_host_from_multi_response(multi_response_data) assert_host_was_created(create_host_response) created_host_id = create_host_response["host"]["id"] retrieved_host = db_get_host(created_host_id) assert old_stale_timestamp == retrieved_host.stale_timestamp assert old_reporter == retrieved_host.reporter host.stale_timestamp = new_stale_timestamp.isoformat() host.reporter = new_reporter multi_response_status, multi_response_data = api_create_or_update_host( [host]) assert_response_status(multi_response_status, 207) update_host_response = get_host_from_multi_response(multi_response_data) assert_host_was_updated(create_host_response, update_host_response) retrieved_host = db_get_host(created_host_id) assert new_stale_timestamp == retrieved_host.stale_timestamp assert new_reporter == retrieved_host.reporter
def test_update_existing_host_dont_change_display_name(db_create_host): # Create an "existing" host fqdn = "host1.domain1.com" display_name = "foo" existing_host = db_create_host(extra_data={ "canonical_facts": { "fqdn": fqdn }, "display_name": display_name }) # Attempt to update the display name from Satellite reporter (shouldn't change) expected_fqdn = "different.domain1.com" input_host = Host({"fqdn": expected_fqdn}, display_name="dont_change_me", reporter="yupana", stale_timestamp=now()) existing_host.update(input_host) # assert display name hasn't changed assert existing_host.display_name == display_name