Пример #1
0
class SourceStatus:
    """Source Status."""
    def __init__(self, request, source_id):
        """Initialize source id."""
        self.request = request
        self.user = request.user
        self.source_id = source_id
        self.source = Sources.objects.get(source_id=source_id)
        self.sources_client = SourcesHTTPClient(self.source.auth_header,
                                                source_id=source_id)

    @property
    def sources_response(self):
        return self.sources_client.build_source_status(self.status())

    def _set_provider_active_status(self, active_status):
        """Set provider active status."""
        if self.source.koku_uuid:
            try:
                provider = Provider.objects.get(uuid=self.source.koku_uuid)
                provider.active = active_status
                provider.save()
            except Provider.DoesNotExist:
                LOG.info(
                    f"No provider found for Source ID: {self.source.source_id}"
                )

    def determine_status(self, provider_type, source_authentication,
                         source_billing_source):
        """Check cloud configuration status."""
        interface = ProviderAccessor(provider_type)
        error_obj = None
        try:
            interface.cost_usage_source_ready(source_authentication,
                                              source_billing_source)
            self._set_provider_active_status(True)
        except ValidationError as validation_error:
            self._set_provider_active_status(False)
            error_obj = validation_error
        return error_obj

    def status(self):
        """Find the source's availability status."""
        source_billing_source = self.source.billing_source.get(
            "data_source") or {}
        source_authentication = self.source.authentication.get(
            "credentials") or {}
        provider_type = self.source.source_type
        return self.determine_status(provider_type, source_authentication,
                                     source_billing_source)

    def push_status(self):
        """Push status_msg to platform sources."""
        try:
            status_obj = self.status()
            self.sources_client.set_source_status(status_obj)
        except SourcesHTTPClientError as error:
            err_msg = "Unable to push source status. Reason: {}".format(
                str(error))
            LOG.warning(err_msg)
Пример #2
0
 def test_set_source_status_patch_fail(self):
     """Test to set source status where the patch fails."""
     test_source_id = 1
     application_type_id = 2
     application_id = 3
     status = 'available'
     error_msg = 'my error'
     client = SourcesHTTPClient(auth_header=Config.SOURCES_FAKE_HEADER,
                                source_id=test_source_id)
     with requests_mock.mock() as m:
         m.get((
             f'http://www.sources.com/api/v1.0/applications?'
             f'filter[application_type_id]={application_type_id}&filter[source_id]={test_source_id}'
         ),
               status_code=200,
               json={'data': [{
                   'id': application_id
               }]})
         m.patch(
             f'http://www.sources.com/api/v1.0/applications/{application_id}',
             status_code=400,
             json={
                 'availability_status': status,
                 'availability_status_error': str(error_msg)
             })
         with self.assertRaises(SourcesHTTPClientError):
             client.set_source_status(error_msg, application_type_id)
Пример #3
0
 def test_set_source_status_patch_fail(self, *args):
     """Test to set source status where the patch fails."""
     test_source_id = 1
     client = SourcesHTTPClient(auth_header=Config.SOURCES_FAKE_HEADER,
                                source_id=test_source_id)
     with requests_mock.mock() as m:
         application_type_id = 2
         application_id = 3
         m.get(
             (f"{MOCK_URL}/api/v1.0/{ENDPOINT_APPLICATIONS}?"
              f"filter[application_type_id]={application_type_id}&filter[source_id]={test_source_id}"
              ),
             status_code=200,
             json={"data": [{
                 "id": application_id
             }]},
         )
         status = "unavailable"
         error_msg = "my error"
         m.patch(
             f"{MOCK_URL}/api/v1.0/{ENDPOINT_APPLICATIONS}/{application_id}",
             status_code=400,
             json={
                 "availability_status": status,
                 "availability_status_error": str(error_msg)
             },
         )
         with self.assertRaises(SourcesHTTPClientError):
             client.set_source_status(error_msg, application_type_id)
Пример #4
0
 def test_set_source_status_patch_missing_application(self, *args):
     """Test to set source status where the patch encounters an application 404."""
     test_source_id = 1
     client = SourcesHTTPClient(auth_header=Config.SOURCES_FAKE_HEADER,
                                source_id=test_source_id)
     with requests_mock.mock() as m:
         application_type_id = 2
         application_id = 3
         m.get(
             (f"{MOCK_URL}/api/v1.0/{ENDPOINT_APPLICATIONS}?"
              f"filter[application_type_id]={application_type_id}&filter[source_id]={test_source_id}"
              ),
             status_code=200,
             json={"data": [{
                 "id": application_id
             }]},
         )
         m.patch(
             f"{MOCK_URL}/api/v1.0/{ENDPOINT_APPLICATIONS}/{application_id}",
             status_code=404)
         with self.assertLogs("sources.sources_http_client",
                              "INFO") as captured_logs:
             error_msg = "my error"
             client.set_source_status(error_msg, application_type_id)
         self.assertIn("[set_source_status] error",
                       captured_logs.output[-1])
Пример #5
0
 def test_set_source_status_patch_fail(self):
     """Test to set source status where the patch fails."""
     test_source_id = 1
     application_type_id = 2
     application_id = 3
     status = "unavailable"
     error_msg = "my error"
     source = Sources.objects.create(source_id=test_source_id,
                                     offset=42,
                                     source_type="AWS")
     source.save()
     client = SourcesHTTPClient(auth_header=Config.SOURCES_FAKE_HEADER,
                                source_id=test_source_id)
     with requests_mock.mock() as m:
         m.get(
             (f"http://www.sources.com/api/v1.0/applications?"
              f"filter[application_type_id]={application_type_id}&filter[source_id]={test_source_id}"
              ),
             status_code=200,
             json={"data": [{
                 "id": application_id
             }]},
         )
         m.patch(
             f"http://www.sources.com/api/v1.0/applications/{application_id}",
             status_code=400,
             json={
                 "availability_status": status,
                 "availability_status_error": str(error_msg)
             },
         )
         with self.assertRaises(SourcesHTTPClientError):
             client.set_source_status(error_msg, application_type_id)
Пример #6
0
def execute_koku_provider_op(msg, cost_management_type_id):
    """
    Execute the 'create' or 'destroy Koku-Provider operations.

    'create' operations:
        Koku POST /providers is executed along with updating the Sources database table with
        the Koku Provider uuid.
    'destroy' operations:
        Koku DELETE /providers is executed along with removing the Sources database entry.

    Two types of exceptions are handled for Koku HTTP operations.  Recoverable client and
    Non-Recoverable client errors.  If the error is recoverable the calling function
    (synchronize_sources) will re-queue the operation.

    Args:
        msg (Asyncio msg): Dictionary messages containing operation,
                                       provider and offset.
            example: {'operation': 'create', 'provider': SourcesModelObj, 'offset': 3}
        cost_management_type_id (Integer): Cost Management Type Identifier

    Returns:
        None

    """
    provider = msg.get("provider")
    operation = msg.get("operation")

    try:
        if operation == "create":
            task = create_or_update_provider.delay(provider.source_id)
            LOG.info(
                f"Creating Koku Provider for Source ID: {str(provider.source_id)} in task: {task.id}"
            )
        elif operation == "update":
            task = create_or_update_provider.delay(provider.source_id)
            LOG.info(
                f"Updating Koku Provider for Source ID: {str(provider.source_id)} in task: {task.id}"
            )
        elif operation == "destroy":
            task = delete_source_and_provider.delay(provider.source_id,
                                                    provider.source_uuid,
                                                    provider.auth_header)
            LOG.info(
                f"Deleting Koku Provider/Source for Source ID: {str(provider.source_id)} in task: {task.id}"
            )
        else:
            LOG.error(f"unknown operation: {operation}")

    except RabbitOperationalError:
        err_msg = f"RabbitmQ unavailable. Unable to {operation} Source ID: {provider.source_id}"
        LOG.error(err_msg)
        sources_network = SourcesHTTPClient(provider.auth_header,
                                            provider.source_id)
        sources_network.set_source_status(
            err_msg, cost_management_type_id=cost_management_type_id)

        # Re-raise exception so it can be re-queued in synchronize_sources
        raise RabbitOperationalError(err_msg)
Пример #7
0
def save_auth_info(auth_header, source_id):
    """
    Store Sources Authentication information given an Source ID.

    This method is called when a Cost Management application is
    attached to a given Source as well as when an Authentication
    is created.  We have to handle both cases since an
    Authentication.create event can occur before a Source is
    attached to the Cost Management application.

    Authentication is stored in the Sources database table.

    Args:
        source_id (Integer): Platform Sources ID.
        auth_header (String): Authentication Header.

    Returns:
        None

    """
    source_type = storage.get_source_type(source_id)

    if source_type:
        sources_network = SourcesHTTPClient(auth_header, source_id)
    else:
        LOG.info(f"Source ID not found for ID: {source_id}")
        return

    try:
        if source_type == Provider.PROVIDER_OCP:
            source_details = sources_network.get_source_details()
            if source_details.get("source_ref"):
                authentication = {
                    "resource_name": source_details.get("source_ref")
                }
            else:
                raise SourcesHTTPClientError("Unable to find Cluster ID")
        elif source_type in (Provider.PROVIDER_AWS,
                             Provider.PROVIDER_AWS_LOCAL):
            authentication = {
                "resource_name": sources_network.get_aws_role_arn()
            }
        elif source_type in (Provider.PROVIDER_AZURE,
                             Provider.PROVIDER_AZURE_LOCAL):
            authentication = {
                "credentials": sources_network.get_azure_credentials()
            }
        else:
            LOG.error(f"Unexpected source type: {source_type}")
            return
        storage.add_provider_sources_auth_info(source_id, authentication)
        storage.clear_update_flag(source_id)
        LOG.info(f"Authentication attached to Source ID: {source_id}")
    except SourcesHTTPClientError as error:
        LOG.info(
            f"Authentication info not available for Source ID: {source_id}")
        sources_network.set_source_status(str(error))
Пример #8
0
def execute_koku_provider_op(msg, cost_management_type_id):
    """
    Execute the 'create' or 'destroy Koku-Provider operations.

    'create' operations:
        Koku POST /providers is executed along with updating the Sources database table with
        the Koku Provider uuid.
    'destroy' operations:
        Koku DELETE /providers is executed along with removing the Sources database entry.

    Two types of exceptions are handled for Koku HTTP operations.  Recoverable client and
    Non-Recoverable client errors.  If the error is recoverable the calling function
    (synchronize_sources) will re-queue the operation.

    Args:
        msg (Asyncio msg): Dictionary messages containing operation,
                                       provider and offset.
            example: {'operation': 'create', 'provider': SourcesModelObj, 'offset': 3}
        cost_management_type_id (Integer): Cost Management Type Identifier

    Returns:
        None

    """
    provider = msg.get('provider')
    operation = msg.get('operation')
    koku_client = KokuHTTPClient(provider.auth_header)
    sources_client = SourcesHTTPClient(provider.auth_header, provider.source_id)
    try:
        if operation == 'create':
            LOG.info(f'Creating Koku Provider for Source ID: {str(provider.source_id)}')
            koku_details = koku_client.create_provider(provider.name, provider.source_type, provider.authentication,
                                                       provider.billing_source, provider.source_uuid)
            LOG.info(f'Koku Provider UUID {koku_details.get("uuid")} assigned to Source ID {str(provider.source_id)}.')
            storage.add_provider_koku_uuid(provider.source_id, koku_details.get('uuid'))
        elif operation == 'destroy':
            if provider.koku_uuid:
                try:
                    response = koku_client.destroy_provider(provider.koku_uuid)
                    LOG.info(
                        f'Koku Provider UUID ({provider.koku_uuid}) Removal Status Code: {str(response.status_code)}')
                except KokuHTTPClientNonRecoverableError:
                    LOG.info(f'Koku Provider already removed.  Remove Source ID: {str(provider.source_id)}.')
            storage.destroy_provider_event(provider.source_id)
        elif operation == 'update':
            koku_details = koku_client.update_provider(provider.koku_uuid, provider.name, provider.source_type,
                                                       provider.authentication, provider.billing_source)
            storage.clear_update_flag(provider.source_id)
            LOG.info(f'Koku Provider UUID {koku_details.get("uuid")} with Source ID {str(provider.source_id)} updated.')
        sources_client.set_source_status(None, cost_management_type_id)

    except KokuHTTPClientError as koku_error:
        raise SourcesIntegrationError('Koku provider error: ', str(koku_error))
    except KokuHTTPClientNonRecoverableError as koku_error:
        err_msg = f'Unable to {operation} provider for Source ID: {str(provider.source_id)}. Reason: {str(koku_error)}'
        LOG.error(err_msg)
        sources_client.set_source_status(str(koku_error), cost_management_type_id)
Пример #9
0
class SourceStatus:
    """Source Status."""

    def __init__(self, request, source_id):
        """Initialize source id."""
        self.request = request
        self.user = request.user
        self.source_id = source_id
        self.source = Sources.objects.get(source_id=source_id)
        self.sources_client = SourcesHTTPClient(self.source.auth_header, source_id=source_id)

    @property
    def sources_response(self):
        return self.sources_client.build_source_status(self.status())

    def determine_status(self, provider, source_authentication, source_billing_source):
        """Check cloud configuration status."""
        interface = ProviderAccessor(provider)
        error_obj = None
        try:
            interface.cost_usage_source_ready(source_authentication, source_billing_source)
        except ValidationError as validation_error:
            error_obj = validation_error

        return error_obj

    def status(self):
        """Find the source's availability status."""
        # Get the source billing_source, whether it's named bucket
        if self.source.billing_source.get("bucket"):
            source_billing_source = self.source.billing_source.get("bucket")
        elif self.source.billing_source.get("data_source"):
            source_billing_source = self.source.billing_source.get("data_source")
        else:
            source_billing_source = {}
        # Get the source authentication
        if self.source.authentication.get("resource_name"):
            source_authentication = self.source.authentication.get("resource_name")
        elif self.source.authentication.get("credentials"):
            source_authentication = self.source.authentication.get("credentials")
        else:
            source_authentication = {}
        provider = self.source.source_type

        status_obj = self.determine_status(provider, source_authentication, source_billing_source)
        return status_obj

    def push_status(self):
        """Push status_msg to platform sources."""
        try:
            status_obj = self.status()
            self.sources_client.set_source_status(status_obj)
        except SourcesHTTPClientError as error:
            err_msg = "Unable to push source status. Reason: {}".format(str(error))
            LOG.warning(err_msg)
Пример #10
0
def set_status_for_source(source_id, error_message):
    try:
        instance = Sources.objects.get(source_id=source_id)
    except Exception as e:
        LOG.error(f"[set_status_for_source] This Source ID {source_id} should exist. error: {e}")
        return

    LOG.info(f"Setting availability status for Source ID: {source_id}")
    client = SourcesHTTPClient(instance.auth_header, source_id)
    try:
        client.set_source_status(error_message)
    except SourcesHTTPClientError as err:
        LOG.error(err)
Пример #11
0
def execute_koku_provider_op(msg):
    """
    Execute the 'create' or 'destroy Koku-Provider operations.

    'create' operations:
        Koku POST /providers is executed along with updating the Sources database table with
        the Koku Provider uuid.
    'destroy' operations:
        Koku DELETE /providers is executed along with removing the Sources database entry.

    Two types of exceptions are handled for Koku HTTP operations.  Recoverable client and
    Non-Recoverable client errors.  If the error is recoverable the calling function
    (synchronize_sources) will re-queue the operation.

    Args:
        msg (Asyncio msg): Dictionary messages containing operation,
                                       provider and offset.
            example: {'operation': 'create', 'provider': SourcesModelObj, 'offset': 3}

    Returns:
        None

    """
    provider = msg.get("provider")
    operation = msg.get("operation")
    account_coordinator = SourcesProviderCoordinator(provider.source_id, provider.auth_header)
    sources_client = SourcesHTTPClient(provider.auth_header, provider.source_id)

    try:
        if operation == "create":
            LOG.info(f"Creating Koku Provider for Source ID: {str(provider.source_id)}")
            instance = account_coordinator.create_account(provider)
            LOG.info(f"Creating provider {instance.uuid} for Source ID: {provider.source_id}")
        elif operation == "update":
            instance = account_coordinator.update_account(provider)
            LOG.info(f"Updating provider {instance.uuid} for Source ID: {provider.source_id}")
        elif operation == "destroy":
            account_coordinator.destroy_account(provider)
            LOG.info(f"Destroying provider {provider.koku_uuid} for Source ID: {provider.source_id}")
        else:
            LOG.error(f"unknown operation: {operation}")
        sources_client.set_source_status(None)

    except SourcesProviderCoordinatorError as account_error:
        raise SourcesIntegrationError("Koku provider error: ", str(account_error))
    except ValidationError as account_error:
        err_msg = (
            f"Unable to {operation} provider for Source ID: {str(provider.source_id)}. Reason: {str(account_error)}"
        )
        LOG.warning(err_msg)
        sources_client.set_source_status(account_error)
Пример #12
0
class SourceStatus:
    """Source Status."""
    def __init__(self, request, source_id):
        """Initialize source id."""
        self.request = request
        self.user = request.user
        self.source_id = source_id
        self.source = Sources.objects.get(source_id=source_id)
        self.auth_header = request.headers.get("X-Rh-Identity")
        self.sources_client = SourcesHTTPClient(auth_header=self.auth_header,
                                                source_id=source_id)

    def status(self):
        """Find the source's availability status."""
        # Get the source billing_source, whether it's named bucket
        if self.source.billing_source.get("bucket"):
            source_billing_source = self.source.billing_source.get("bucket")
        elif self.source.billing_source.get("data_source"):
            source_billing_source = self.source.billing_source.get(
                "data_source")
        else:
            source_billing_source = {}
        # Get the source authentication
        if self.source.authentication.get("resource_name"):
            source_authentication = self.source.authentication.get(
                "resource_name")
        elif self.source.authentication.get("credentials"):
            source_authentication = self.source.authentication.get(
                "credentials")
        else:
            source_authentication = {}
        provider = self.source.source_type

        interface = ProviderAccessor(provider)

        availability_status = interface.availability_status(
            source_authentication, source_billing_source)
        return availability_status

    def push_status(self):
        """Push status_msg to platform sources."""
        try:
            status_obj = self.status()
            status_msg = status_obj.get("availability_status_error",
                                        "Status unavailable")
            self.sources_client.set_source_status(status_msg)
        except SourcesHTTPClientError as error:
            err_msg = "Unable to push source status. Reason: {}".format(
                str(error))
            LOG.warning(err_msg)
Пример #13
0
    def test_set_source_status_unexpected_header(self):
        """Test to set source status with missing account in header."""
        test_source_id = 1
        application_type_id = 2
        error_msg = "my error"
        malformed_identity_header = {
            "not_identity": {
                "type": "User",
                "user": {"username": "******", "email": "*****@*****.**", "is_org_admin": True},
            }
        }
        json_malformed_identity = json_dumps(malformed_identity_header)
        malformed_internal_header = b64encode(json_malformed_identity.encode("utf-8"))
        malformed_auth_header = malformed_internal_header.decode("utf-8")

        missing_account_header = {
            "identity": {
                "type": "User",
                "user": {"username": "******", "email": "*****@*****.**", "is_org_admin": True},
            }
        }
        missing_account_identity = json_dumps(missing_account_header)
        missing_account_internal_header = b64encode(missing_account_identity.encode("utf-8"))
        missing_account_auth_header = missing_account_internal_header.decode("utf-8")

        test_headers = [malformed_auth_header, missing_account_auth_header]
        source = Sources.objects.create(source_id=test_source_id, offset=42, source_type="AWS")
        source.save()

        for header in test_headers:
            client = SourcesHTTPClient(auth_header=header, source_id=test_source_id)
            response = client.set_source_status(error_msg, application_type_id)
            self.assertFalse(response)
Пример #14
0
 def test_set_source_status_branches(self, *args):
     """Test to set source status."""
     test_source_id = 1
     client = SourcesHTTPClient(auth_header=Config.SOURCES_FAKE_HEADER,
                                source_id=test_source_id)
     with requests_mock.mock() as m:
         application_type_id = COST_MGMT_APP_TYPE_ID
         application_id = 3
         m.get(
             (f"{MOCK_URL}/api/v1.0/{ENDPOINT_APPLICATIONS}?"
              f"filter[application_type_id]={application_type_id}&filter[source_id]={test_source_id}"
              ),
             status_code=200,
             json={"data": [{
                 "id": application_id
             }]},
         )
         status = "unavailable"
         error_msg = "my error"
         m.patch(
             f"{MOCK_URL}/api/v1.0/{ENDPOINT_APPLICATIONS}/{application_id}",
             status_code=204,
             json={
                 "availability_status": status,
                 "availability_status_error": str(error_msg)
             },
         )
         response = client.set_source_status(error_msg)
         self.assertTrue(response)
Пример #15
0
class SourceStatus:
    """Source Status."""
    def __init__(self, source_id):
        """Initialize source id."""
        self.source = Sources.objects.get(source_id=source_id)
        self.auth_header = self.source.auth_header
        self.sources_client = SourcesHTTPClient(auth_header=self.auth_header,
                                                source_id=source_id)

    def status(self):
        """Find the source's availability status."""
        # Get the source billing_source, whether it's named bucket
        if self.source.billing_source.get('bucket'):
            source_billing_source = self.source.billing_source.get('bucket')
        elif self.source.billing_source.get('data_source'):
            source_billing_source = self.source.billing_source.get(
                'data_source')
        else:
            source_billing_source = {}
        # Get the source authentication
        if self.source.authentication.get('resource_name'):
            source_authentication = self.source.authentication.get(
                'resource_name')
        elif self.source.authentication.get('credentials'):
            source_authentication = self.source.authentication.get(
                'credentials')
        else:
            source_authentication = {}
        provider = self.source.source_type

        interface = ProviderAccessor(provider)

        availability_status = interface.availability_status(
            source_authentication, source_billing_source)
        return availability_status

    def push_status(self, status_msg):
        """Push status_msg to platform sources."""
        try:
            self.sources_client.set_source_status(status_msg)
        except SourcesHTTPClientError as error:
            err_msg = 'Unable to push source status. Reason: {}'.format(
                str(error))
            LOG.error(err_msg)
Пример #16
0
def save_auth_info(auth_header, source_id):
    """
    Store Sources Authentication information given an Source ID.

    This method is called when a Cost Management application is
    attached to a given Source as well as when an Authentication
    is created.  We have to handle both cases since an
    Authentication.create event can occur before a Source is
    attached to the Cost Management application.

    Authentication is stored in the Sources database table.

    Args:
        source_id (Integer): Platform Sources ID.
        auth_header (String): Authentication Header.

    Returns:
        None

    """
    source_type = storage.get_source_type(source_id)

    if not source_type:
        LOG.info(f"Source ID not found for ID: {source_id}")
        return

    sources_network = SourcesHTTPClient(auth_header, source_id)

    try:
        authentication = get_authentication(source_type, sources_network)
    except SourcesHTTPClientError as error:
        LOG.info(
            f"Authentication info not available for Source ID: {source_id}")
        sources_network.set_source_status(error)
    else:
        if not authentication:
            return
        storage.add_provider_sources_auth_info(source_id, authentication)
        storage.clear_update_flag(source_id)
        LOG.info(f"Authentication attached to Source ID: {source_id}")
Пример #17
0
    def test_set_source_status_patch_missing_application(self):
        """Test to set source status where the patch encounters an application 404."""
        test_source_id = 1
        application_type_id = 2
        application_id = 3

        error_msg = "my error"
        source = Sources.objects.create(source_id=test_source_id, offset=42, source_type="AWS")
        source.save()
        client = SourcesHTTPClient(auth_header=Config.SOURCES_FAKE_HEADER, source_id=test_source_id)
        with requests_mock.mock() as m:
            m.get(
                (
                    f"http://www.sources.com/api/v1.0/applications?"
                    f"filter[application_type_id]={application_type_id}&filter[source_id]={test_source_id}"
                ),
                status_code=200,
                json={"data": [{"id": application_id}]},
            )
            m.patch(f"http://www.sources.com/api/v1.0/applications/{application_id}", status_code=404)
            with self.assertLogs("sources.sources_http_client", "INFO") as captured_logs:
                client.set_source_status(error_msg, application_type_id)
            self.assertIn("Unable to set status for Source", captured_logs.output[-1])
Пример #18
0
 def test_set_source_status_source_deleted(self):
     """Test to set source status after source has been deleted."""
     test_source_id = 1
     application_type_id = 2
     error_msg = 'my error'
     client = SourcesHTTPClient(auth_header=Config.SOURCES_FAKE_HEADER,
                                source_id=test_source_id)
     with requests_mock.mock() as m:
         m.get((
             f'http://www.sources.com/api/v1.0/applications?'
             f'filter[application_type_id]={application_type_id}&filter[source_id]={test_source_id}'
         ),
               status_code=200,
               json={'data': []})
         response = client.set_source_status(error_msg, application_type_id)
         self.assertFalse(response)
Пример #19
0
 def test_set_source_status_source_deleted(self, *args):
     """Test to set source status after source has been deleted on platform."""
     test_source_id = 1
     client = SourcesHTTPClient(auth_header=Config.SOURCES_FAKE_HEADER,
                                source_id=test_source_id)
     with requests_mock.mock() as m:
         application_type_id = 2
         m.get(
             (f"{MOCK_URL}/api/v1.0/{ENDPOINT_APPLICATIONS}?"
              f"filter[application_type_id]={application_type_id}&filter[source_id]={test_source_id}"
              ),
             status_code=200,
             json={"data": []},
         )
         error_msg = "my error"
         response = client.set_source_status(error_msg, application_type_id)
         self.assertFalse(response)
Пример #20
0
 def test_set_source_status_source_deleted(self):
     """Test to set source status after source has been deleted."""
     test_source_id = 1
     application_type_id = 2
     error_msg = "my error"
     source = Sources.objects.create(source_id=test_source_id, offset=42, source_type="AWS")
     source.save()
     client = SourcesHTTPClient(auth_header=Config.SOURCES_FAKE_HEADER, source_id=test_source_id)
     with requests_mock.mock() as m:
         m.get(
             (
                 f"http://www.sources.com/api/v1.0/applications?"
                 f"filter[application_type_id]={application_type_id}&filter[source_id]={test_source_id}"
             ),
             status_code=200,
             json={"data": []},
         )
         response = client.set_source_status(error_msg, application_type_id)
         self.assertFalse(response)
Пример #21
0
 def test_set_source_status(self):
     """Test to set source status."""
     test_source_id = 1
     application_type_id = 2
     application_id = 3
     status = "available"
     error_msg = "my error"
     client = SourcesHTTPClient(auth_header=Config.SOURCES_FAKE_HEADER, source_id=test_source_id)
     with requests_mock.mock() as m:
         m.get(
             (
                 f"http://www.sources.com/api/v1.0/applications?"
                 f"filter[application_type_id]={application_type_id}&filter[source_id]={test_source_id}"
             ),
             status_code=200,
             json={"data": [{"id": application_id}]},
         )
         m.patch(
             f"http://www.sources.com/api/v1.0/applications/{application_id}",
             status_code=204,
             json={"availability_status": status, "availability_status_error": str(error_msg)},
         )
         response = client.set_source_status(error_msg, application_type_id)
         self.assertTrue(response)
Пример #22
0
 def test_set_source_status_failed_header(self):
     """Test set_source_status with invalid header."""
     client = SourcesHTTPClient(auth_header=Config.SOURCES_FAKE_HEADER)
     with patch.object(SourcesHTTPClient, "build_status_header", return_value=None):
         self.assertFalse(client.set_source_status(""))
Пример #23
0
class SourceStatus:
    """Source Status."""
    def __init__(self, source_id):
        """Initialize source id."""
        self.source_id = source_id
        self.source = Sources.objects.get(source_id=source_id)
        if not source_settings_complete(
                self.source) or self.source.pending_delete:
            raise ObjectDoesNotExist(
                f"Source ID: {self.source_id} not ready for status")
        self.sources_client = SourcesHTTPClient(self.source.auth_header,
                                                source_id=source_id)

    @property
    def sources_response(self):
        return self.sources_client.build_source_status(self.status())

    def _set_provider_active_status(self, active_status):
        """Set provider active status."""
        if self.source.koku_uuid:
            try:
                provider = Provider.objects.get(uuid=self.source.koku_uuid)
                provider.active = active_status
                provider.save()
            except Provider.DoesNotExist:
                LOG.info(
                    f"No provider found for Source ID: {self.source.source_id}"
                )

    def determine_status(self, provider_type, source_authentication,
                         source_billing_source):
        """Check cloud configuration status."""
        interface = ProviderAccessor(provider_type)
        error_obj = None
        try:
            if self.source.account_id not in settings.DEMO_ACCOUNTS:
                interface.cost_usage_source_ready(source_authentication,
                                                  source_billing_source)
            self._set_provider_active_status(True)
        except ValidationError as validation_error:
            self._set_provider_active_status(False)
            error_obj = validation_error
        self.source.refresh_from_db()
        return error_obj

    def status(self):
        """Find the source's availability status."""
        source_billing_source = self.source.billing_source.get(
            "data_source") or {}
        source_authentication = self.source.authentication.get(
            "credentials") or {}
        provider_type = self.source.source_type
        return self.determine_status(provider_type, source_authentication,
                                     source_billing_source)

    @transaction.atomic
    def update_source_name(self):
        """Update source name if it is out of sync with platform."""
        source_details = self.sources_client.get_source_details()
        if source_details.get("name") != self.source.name:
            self.source.name = source_details.get("name")
            self.source.save()
            builder = SourcesProviderCoordinator(self.source_id,
                                                 self.source.auth_header)
            builder.update_account(self.source)

    def push_status(self):
        """Push status_msg to platform sources."""
        try:
            status_obj = self.status()
            if self.source.source_type in (Provider.PROVIDER_GCP,
                                           Provider.PROVIDER_GCP_LOCAL):
                builder = SourcesProviderCoordinator(self.source.source_id,
                                                     self.source.auth_header)
                if not status_obj:
                    if self.source.koku_uuid:
                        builder.update_account(self.source)
                    elif self.source.billing_source.get("data_source",
                                                        {}).get("table_id"):
                        builder.create_account(self.source)
            self.sources_client.set_source_status(status_obj)
            self.update_source_name()
            LOG.info(
                f"Source status for Source ID: {str(self.source_id)}: Status: {str(status_obj)}"
            )
        except SkipStatusPush as error:
            LOG.info(
                f"Platform sources status push skipped. Reason: {str(error)}")
        except SourcesHTTPClientError as error:
            err_msg = "Unable to push source status. Reason: {}".format(
                str(error))
            LOG.warning(err_msg)