diff --git a/cloud_governance/common/clouds/aws/price/price.py b/cloud_governance/common/clouds/aws/price/price.py index 76ed2d48..0510015a 100644 --- a/cloud_governance/common/clouds/aws/price/price.py +++ b/cloud_governance/common/clouds/aws/price/price.py @@ -6,6 +6,7 @@ import json from pkg_resources import resource_filename +from cloud_governance.common.logger.init_logger import logger # Search product filter from cloud_governance.main.environment_variables import environment_variables @@ -123,3 +124,24 @@ def get_ec2_price(self, resource: str, item_data: dict): if item_data['VolumeType'] == 'sc1': ebs_monthly_cost = self.get_ebs_cost(volume_type='sc1', region=self.region) * item_data['Size'] return round(ebs_monthly_cost, 3) + + def get_service_pricing(self, service_code: str, filter_list: list) -> float: + """ + This method returns the price of the product by service + :param service_code: + :type service_code: + :param filter_list: + :type filter_list: + :return: + :rtype: + """ + try: + data = self.__client.get_products(ServiceCode=service_code, Filters=filter_list) + od = json.loads(data['PriceList'][0])['terms']['OnDemand'] + id1 = list(od)[0] + id2 = list(od[id1]['priceDimensions'])[0] + return float(od[id1]['priceDimensions'][id2]['pricePerUnit']['USD']) + except Exception as err: + print(err) + logger.error(err) + return 0 diff --git a/cloud_governance/common/clouds/aws/price/resources_pricing.py b/cloud_governance/common/clouds/aws/price/resources_pricing.py index 4534a32c..b13da57d 100644 --- a/cloud_governance/common/clouds/aws/price/resources_pricing.py +++ b/cloud_governance/common/clouds/aws/price/resources_pricing.py @@ -1,5 +1,6 @@ from cloud_governance.common.clouds.aws.price.price import AWSPrice +from cloud_governance.common.utils.configs import DEFAULT_ROUND_DIGITS from cloud_governance.main.environment_variables import environment_variables @@ -21,7 +22,8 @@ def ec2_instance_type_cost(self, instance_type: str, hours: float): This method returns the cost of ec2 instance types cost @return: """ - cost = float(self._aws_pricing.get_price(instance=instance_type, os='Linux', region=self._aws_pricing.get_region_name(self.region))) + cost = float(self._aws_pricing.get_price(instance=instance_type, os='Linux', + region=self._aws_pricing.get_region_name(self.region))) return cost * hours def get_ebs_cost(self, volume_size: int, volume_type: str, hours: float): @@ -45,3 +47,27 @@ def get_const_prices(self, resource_type: str, hours: int): if resource_type == 'eip': return self.IP_HOURLY_COST * hours + def get_eip_unit_price(self): + """ + This method returns the ElasticIp Price + :return: + :rtype: + """ + return self.IP_HOURLY_COST + + def get_nat_gateway_unit_price(self, region_name: str): + """ + This method returns the unit price of NatGateway + :param region_name: + :type region_name: + :return: + :rtype: + """ + service_code = 'AmazonEC2' + filter_dict = [ + {"Field": "productFamily", "Value": "NAT Gateway", "Type": "TERM_MATCH"}, + {"Field": "regionCode", "Value": region_name, "Type": "TERM_MATCH"}, + {"Field": "groupDescription", "Value": "Hourly charge for NAT Gateways", "Type": "TERM_MATCH"}, + ] + unit_price = self._aws_pricing.get_service_pricing(service_code, filter_dict) + return round(unit_price, DEFAULT_ROUND_DIGITS) diff --git a/cloud_governance/policy/aws/cleanup/unused_nat_gateway.py b/cloud_governance/policy/aws/cleanup/unused_nat_gateway.py index 26d47de8..f51bbcb8 100644 --- a/cloud_governance/policy/aws/cleanup/unused_nat_gateway.py +++ b/cloud_governance/policy/aws/cleanup/unused_nat_gateway.py @@ -58,6 +58,7 @@ def run_policy_operations(self): :return: :rtype: """ + unit_price = self._resource_pricing.get_nat_gateway_unit_price(region_name=self._region) unused_nat_gateways = [] nat_gateways = self._ec2_operations.get_nat_gateways() for nat_gateway in nat_gateways: @@ -83,7 +84,8 @@ def run_policy_operations(self): resource_action=self.RESOURCE_ACTION, cloud_name=self._cloud_name, resource_type='NatGateway', - resource_state=nat_gateway.get('State') + resource_state=nat_gateway.get('State'), + unit_price=unit_price if not cleanup_result else "Deleted") unused_nat_gateways.append(resource_data) if not cleanup_result: diff --git a/cloud_governance/policy/aws/ip_unattached.py b/cloud_governance/policy/aws/ip_unattached.py index 87d01370..9c884de9 100644 --- a/cloud_governance/policy/aws/ip_unattached.py +++ b/cloud_governance/policy/aws/ip_unattached.py @@ -18,6 +18,7 @@ def run_policy_operations(self): :return: :rtype: """ + unit_price = self._resource_pricing.get_eip_unit_price() addresses = self._ec2_operations.get_elastic_ips() active_cluster_ids = self._get_active_cluster_ids() unattached_addresses = [] @@ -43,6 +44,7 @@ def run_policy_operations(self): resource_action=self.RESOURCE_ACTION, cloud_name=self._cloud_name, resource_type='PublicIPv4', + unit_price=unit_price, resource_state='disassociated' if not cleanup_result else "Deleted" ) unattached_addresses.append(resource_data) diff --git a/cloud_governance/policy/helpers/abstract_policy_operations.py b/cloud_governance/policy/helpers/abstract_policy_operations.py index 1b27a208..cb92bcca 100644 --- a/cloud_governance/policy/helpers/abstract_policy_operations.py +++ b/cloud_governance/policy/helpers/abstract_policy_operations.py @@ -172,6 +172,18 @@ def _get_all_volumes(self): """ raise NotImplementedError("This method not yet implemented") + def __current_savings_year(self, unit_price: float): + """ + This method returns the savings this year + :param unit_price: + :type unit_price: + :return: + :rtype: + """ + year_end_date = datetime.utcnow().date().replace(month=12, day=31) + total_days = (year_end_date - datetime.utcnow().date()).days + 1 + return total_days * unit_price + # ES Schema format def _get_es_schema(self, resource_id: str, user: str, skip_policy: str, cleanup_days: int, dry_run: str, @@ -193,6 +205,8 @@ def _get_es_schema(self, resource_id: str, user: str, skip_policy: str, cleanup_ f'Resource{resource_action}': cleanup_result, 'PublicCloud': cloud_name, 'ExpireDays': self._days_to_take_action, + 'UnitPrice': kwargs.get('unit_price', 0), + 'TotalYearlySavings': self.__current_savings_year(kwargs.get('unit_price', 0)), 'index-id': f'{current_date}-{cloud_name.lower()}-{self.account.lower()}-{region.lower()}-{resource_id}-{resource_state.lower()}' } if kwargs.get('launch_time'): diff --git a/cloud_governance/policy/helpers/aws/aws_policy_operations.py b/cloud_governance/policy/helpers/aws/aws_policy_operations.py index c6ed507b..70bafadf 100644 --- a/cloud_governance/policy/helpers/aws/aws_policy_operations.py +++ b/cloud_governance/policy/helpers/aws/aws_policy_operations.py @@ -3,6 +3,7 @@ from cloud_governance.common.clouds.aws.cloudwatch.cloudwatch_operations import CloudWatchOperations from cloud_governance.common.clouds.aws.ec2.ec2_operations import EC2Operations +from cloud_governance.common.clouds.aws.price.resources_pricing import ResourcesPricing from cloud_governance.common.clouds.aws.s3.s3_operations import S3Operations from cloud_governance.policy.helpers.abstract_policy_operations import AbstractPolicyOperations from cloud_governance.common.logger.init_logger import logger @@ -18,6 +19,7 @@ def __init__(self): self._ec2_client = boto3.client('ec2', region_name=self._region) self._ec2_operations = EC2Operations(region=self._region) self._cloudwatch = CloudWatchOperations(region=self._region) + self._resource_pricing = ResourcesPricing() self._s3_client = boto3.client('s3') self._iam_client = boto3.client('iam')