def __init__( self, tenant_id, client_id, client_secret, resource_group_name, storage_account_name, subscription_id=None, cloud="public", ): """Establish connection information.""" self._resource_group_name = resource_group_name self._storage_account_name = storage_account_name self._factory = AzureClientFactory(subscription_id, tenant_id, client_id, client_secret, cloud) if not self._factory.subscription_id: raise AzureServiceError("Azure Service missing subscription id.") self._cloud_storage_account = self._factory.cloud_storage_account( resource_group_name, storage_account_name) if not self._factory.credentials: raise AzureServiceError( "Azure Service credentials are not configured.")
def __init__(self, subscription_id, tenant_id, client_id, client_secret, resource_group_name, storage_account_name, cloud='public'): """Establish connection information.""" self._resource_group_name = resource_group_name self._storage_account_name = storage_account_name self._factory = AzureClientFactory(subscription_id, tenant_id, client_id, client_secret, cloud) self._cloud_storage_account = self._factory.cloud_storage_account( resource_group_name, storage_account_name) try: self._blockblob_service = self._cloud_storage_account.create_block_blob_service( ) except AzureException as error: raise AzureServiceError( 'Unable to create block blob service. Error: %s', str(error)) if not self._factory.credentials: raise AzureServiceError( 'Azure Service credentials are not configured.')
def test_cloud_storage_account(self, _): """Test the cloud_storage_account method.""" subscription_id = FAKE.uuid4() resource_group_name = FAKE.word() storage_account_name = FAKE.word() obj = AzureClientFactory( subscription_id=subscription_id, tenant_id=FAKE.uuid4(), client_id=FAKE.uuid4(), client_secret=FAKE.word(), cloud=random.choice(self.clouds), ) with patch.object(StorageManagementClient, "storage_accounts", return_value=None): cloud_account = obj.cloud_storage_account(resource_group_name, storage_account_name) self.assertTrue(isinstance(cloud_account, BlobServiceClient))
def test_constructor(self, _): """Test that we can create an AzureClientFactory object.""" obj = AzureClientFactory(subscription_id=FAKE.uuid4(), tenant_id=FAKE.uuid4(), client_id=FAKE.uuid4(), client_secret=FAKE.word(), cloud=random.choice(self.clouds)) self.assertTrue(isinstance(obj, AzureClientFactory))
def test_subscription_id(self, _): """Test the subscription_id property.""" subscription_id = FAKE.uuid4() obj = AzureClientFactory(subscription_id=subscription_id, tenant_id=FAKE.uuid4(), client_id=FAKE.uuid4(), client_secret=FAKE.word(), cloud=random.choice(self.clouds)) self.assertTrue(obj.subscription_id, subscription_id)
def test_storage_client(self, _): """Test the storage_client property.""" obj = AzureClientFactory(subscription_id=FAKE.uuid4(), tenant_id=FAKE.uuid4(), client_id=FAKE.uuid4(), client_secret=FAKE.word(), cloud=random.choice(self.clouds)) self.assertTrue(isinstance(obj.storage_client, StorageManagementClient))
def test_credentials(self, _): """Test the credentials property.""" obj = AzureClientFactory(subscription_id=FAKE.uuid4(), tenant_id=FAKE.uuid4(), client_id=FAKE.uuid4(), client_secret=FAKE.word(), cloud=random.choice(self.clouds)) self.assertTrue( isinstance(obj._credentials, ServicePrincipalCredentials))
class AzureService: """A class to handle interactions with the Azure services.""" def __init__(self, subscription_id, tenant_id, client_id, client_secret, resource_group_name, storage_account_name, cloud='public'): """Establish connection information.""" self._resource_group_name = resource_group_name self._storage_account_name = storage_account_name self._factory = AzureClientFactory(subscription_id, tenant_id, client_id, client_secret, cloud) self._cloud_storage_account = self._factory.cloud_storage_account( resource_group_name, storage_account_name) try: self._blockblob_service = self._cloud_storage_account.create_block_blob_service( ) except AzureException as error: raise AzureServiceError( 'Unable to create block blob service. Error: %s', str(error)) if not self._factory.credentials: raise AzureServiceError( 'Azure Service credentials are not configured.') def get_cost_export_for_key(self, key, container_name): """Get the latest cost export file from given storage account container.""" report = None blob_list = self._blockblob_service.list_blobs(container_name) for blob in blob_list: if key == blob.name: report = blob break if not report: message = f'No cost report for report name {key} found in container {container_name}.' raise AzureCostReportNotFound(message) return report def download_cost_export(self, key, container_name, destination=None): """Download the latest cost export file from a given storage container.""" cost_export = self.get_cost_export_for_key(key, container_name) file_path = destination if not destination: temp_file = NamedTemporaryFile(delete=False, suffix='.csv') file_path = temp_file.name try: self._blockblob_service.get_blob_to_path(container_name, cost_export.name, file_path) except AzureException as error: raise AzureServiceError('Failed to download cost export. Error: ', str(error)) return file_path def get_latest_cost_export_for_path(self, report_path, container_name): """Get the latest cost export file from given storage account container.""" latest_report = None blob_list = self._blockblob_service.list_blobs(container_name) for blob in blob_list: if report_path in blob.name and not latest_report: latest_report = blob elif report_path in blob.name and blob.properties.last_modified > latest_report.properties.last_modified: latest_report = blob if not latest_report: message = f'No cost report found in container {container_name} for '\ f'path {report_path}.' raise AzureCostReportNotFound(message) return latest_report def describe_cost_management_exports(self): """List cost management export.""" cost_management_client = self._factory.cost_management_client scope = f'/subscriptions/{self._factory.subscription_id}' management_reports = cost_management_client.exports.list(scope) expected_resource_id = ( f'/subscriptions/{self._factory.subscription_id}/resourceGroups/' f'{self._resource_group_name}/providers/Microsoft.Storage/' f'storageAccounts/{self._storage_account_name}') export_reports = [] for report in management_reports.value: if report.delivery_info.destination.resource_id == expected_resource_id: report_def = { 'name': report.name, 'container': report.delivery_info.destination.container, 'directory': report.delivery_info.destination.root_folder_path } export_reports.append(report_def) return export_reports
class AzureService: """A class to handle interactions with the Azure services.""" def __init__( self, tenant_id, client_id, client_secret, resource_group_name, storage_account_name, subscription_id=None, cloud="public", ): """Establish connection information.""" self._resource_group_name = resource_group_name self._storage_account_name = storage_account_name self._factory = AzureClientFactory(subscription_id, tenant_id, client_id, client_secret, cloud) if not self._factory.subscription_id: raise AzureServiceError("Azure Service missing subscription id.") self._cloud_storage_account = self._factory.cloud_storage_account(resource_group_name, storage_account_name) if not self._factory.credentials: raise AzureServiceError("Azure Service credentials are not configured.") def get_cost_export_for_key(self, key, container_name): """Get the latest cost export file from given storage account container.""" report = None container_client = self._cloud_storage_account.get_container_client(container_name) blob_list = container_client.list_blobs(name_starts_with=key) for blob in blob_list: if key == blob.name: report = blob break if not report: message = f"No cost report for report name {key} found in container {container_name}." raise AzureCostReportNotFound(message) return report def download_cost_export(self, key, container_name, destination=None): """Download the latest cost export file from a given storage container.""" cost_export = self.get_cost_export_for_key(key, container_name) file_path = destination if not destination: temp_file = NamedTemporaryFile(delete=False, suffix=".csv") file_path = temp_file.name try: blob_client = self._cloud_storage_account.get_blob_client(container_name, cost_export.name) with open(file_path, "wb") as blob_download: blob_download.write(blob_client.download_blob().readall()) except (AzureException, IOError) as error: raise AzureServiceError("Failed to download cost export. Error: ", str(error)) return file_path def get_latest_cost_export_for_path(self, report_path, container_name): """Get the latest cost export file from given storage account container.""" latest_report = None try: container_client = self._cloud_storage_account.get_container_client(container_name) blob_list = container_client.list_blobs(name_starts_with=report_path) for blob in blob_list: if report_path in blob.name and not latest_report: latest_report = blob elif report_path in blob.name and blob.last_modified > latest_report.last_modified: latest_report = blob if not latest_report: message = f"No cost report found in container {container_name} for " f"path {report_path}." raise AzureCostReportNotFound(message) return latest_report except HttpResponseError as httpError: if httpError.status_code == 403: message = ( "An authorization error occurred attempting to gather latest export" f" in container {container_name} for " f"path {report_path}." ) else: message = ( "Unknown error occurred attempting to gather latest export" f" in container {container_name} for " f"path {report_path}." ) error_msg = message + f" Azure Error: {httpError}." LOG.warning(error_msg) raise AzureCostReportNotFound(message) def describe_cost_management_exports(self): """List cost management export.""" cost_management_client = self._factory.cost_management_client scope = f"/subscriptions/{self._factory.subscription_id}" expected_resource_id = ( f"/subscriptions/{self._factory.subscription_id}/resourceGroups/" f"{self._resource_group_name}/providers/Microsoft.Storage/" f"storageAccounts/{self._storage_account_name}" ) export_reports = [] try: management_reports = cost_management_client.exports.list(scope) for report in management_reports.value: if report.delivery_info.destination.resource_id == expected_resource_id: report_def = { "name": report.name, "container": report.delivery_info.destination.container, "directory": report.delivery_info.destination.root_folder_path, } export_reports.append(report_def) except AzureException: raise AzureCostReportNotFound(AzureException) return export_reports