Update invoice handling

This commit is contained in:
Setyo Nugroho 2023-05-23 07:17:59 +00:00
parent 72c28d2b57
commit a1e020ae2d
4 changed files with 200 additions and 76 deletions

View file

@ -0,0 +1,9 @@
from enum import Enum
class UnpainInvoiceAction(Enum):
SEND_MESSAGE = "send_message"
STOP_INSTANCE = "stop_instance"
SUSPEND_INSTANCE = "suspend_instance"
PAUSE_INSTANCE = "pause_instance"
DELETE_INSTANCE = "delete_instance"

View file

@ -0,0 +1,122 @@
import logging
import traceback
import openstack
from django.utils import timezone
from core.feature.unpaid_invoice_handle.actions import UnpainInvoiceAction
from core.models import Invoice
from core.notification import send_notification
from core.utils.dynamic_setting import get_dynamic_setting, BILLING_ENABLED
from yuyu import settings
LOG = logging.getLogger("yuyu")
class UnpaidInvoiceHandlerCommand:
def handle(self):
LOG.info("Processing Unpaid Invoice")
# Initialize connection
if not get_dynamic_setting(BILLING_ENABLED):
return
expired_unpaid_invoices = Invoice.objects.filter(state=Invoice.InvoiceState.UNPAID).all()
for invoice in expired_unpaid_invoices:
self.run_action_on_config(invoice)
LOG.info("Processing Unpaid Invoice Done")
def run_action_on_config(self, invoice):
schedule_config = settings.UNPAID_INVOICE_HANDLER_CONFIG
date_diff = timezone.now() - invoice.end_date
past_day = date_diff.days
for config in schedule_config:
if config['day'] == past_day:
self.run_action(invoice, config['action'], config)
def run_action(self, invoice, action, config=None):
try:
if action == UnpainInvoiceAction.SEND_MESSAGE:
self._send_message(invoice, config)
if action == UnpainInvoiceAction.STOP_INSTANCE:
self._stop_component(invoice)
if action == UnpainInvoiceAction.SUSPEND_INSTANCE:
self._stop_component(invoice)
if action == UnpainInvoiceAction.PAUSE_INSTANCE:
self._stop_component(invoice)
if action == UnpainInvoiceAction.DELETE_INSTANCE:
self._delete_component(invoice)
except Exception:
LOG.exception("Failed to process Unpaid Invoice")
send_notification(
project=None,
title='[Error] Error when processing unpaid invoice',
short_description=f'There is an error when processing unpaid invoice',
content=f'There is an error when handling unpaid invoice \n {traceback.format_exc()}',
)
def _stop_component(self, invoice: Invoice):
LOG.info("Stopping Component")
conn = openstack.connect(cloud=settings.CLOUD_CONFIG_NAME)
# Stop Instance
for server in conn.compute.servers(project_id=invoice.project.tenant_id):
conn.compute.stop_server(server)
def _suspend_component(self, invoice: Invoice):
LOG.info("Suspending Component")
conn = openstack.connect(cloud=settings.CLOUD_CONFIG_NAME)
# Suspend Instance
for server in conn.compute.servers(project_id=invoice.project.tenant_id):
conn.compute.suspend_server(server)
def _pause_component(self, invoice: Invoice):
LOG.info("Pausing Component")
conn = openstack.connect(cloud=settings.CLOUD_CONFIG_NAME)
# Pause Instance
for server in conn.compute.servers(project_id=invoice.project.tenant_id):
conn.compute.pause_server(server)
def _delete_component(self, invoice: Invoice):
LOG.info("Deleting Component")
conn = openstack.connect(cloud=settings.CLOUD_CONFIG_NAME)
# Delete Instance
for server in conn.compute.servers(project_id=invoice.project.tenant_id):
conn.compute.delete_server(server)
# Delete Image
for image in conn.compute.images(project_id=invoice.project.tenant_id):
conn.compute.delete_image(image)
# Delete Floating Ips
for ip in conn.network.ips(project_id=invoice.project.tenant_id):
conn.network.delete_ip(ip)
# Delete Router
for route in conn.network.routers(project_id=invoice.project.tenant_id):
conn.network.delete_router(route)
# Delete Volume
for volume in conn.block_storage.volumes(project_id=invoice.project.tenant_id):
conn.block_storage.delete_volume(volume)
# Delete Snapshot
for snapshot in conn.block_storage.snapshots(project_id=invoice.project.tenant_id):
conn.block_storage.delete_snapshot(snapshot)
def _send_message(self, invoice: Invoice, message_config):
LOG.info("Sending Message")
send_notification(
project=invoice.project,
title=message_config['message_title'],
short_description=message_config['message_short_description'],
content=message_config['message_content'],
)

View file

@ -0,0 +1,64 @@
from datetime import timezone
import logging
from core.feature.unpaid_invoice_handle.actions import UnpainInvoiceAction
from core.feature.unpaid_invoice_handle.command import UnpaidInvoiceHandlerCommand
from core.models import Invoice
from yuyu import settings
LOG = logging.getLogger("yuyu")
class UnpaidInvoiceEventHandler:
filter_action = [
UnpainInvoiceAction.STOP_INSTANCE,
UnpainInvoiceAction.SUSPEND_INSTANCE,
UnpainInvoiceAction.PAUSE_INSTANCE,
UnpainInvoiceAction.DELETE_INSTANCE,
]
def filter_event_project_id(event_type, raw_payload):
if event_type == 'floatingip.create.end':
return raw_payload['floatingip']['tenant_id']
if event_type == 'image.activate':
return raw_payload['owner']
if event_type == 'compute.instance.update':
return raw_payload['tenant_id']
if event_type == 'router.create.end':
return raw_payload['router']['tenant_id']
if event_type == 'router.update.end':
return raw_payload['router']['tenant_id']
if event_type == 'snapshot.create.end':
return raw_payload['tenant_id']
if event_type == 'volume.create.end':
return raw_payload['tenant_id']
return None
def handle(self, event_type, raw_payload):
try:
LOG.exception("Processing Unpaid Invoice Event")
schedule_config = settings.UNPAID_INVOICE_HANDLER_CONFIG
command = UnpaidInvoiceHandlerCommand()
project_id = self.filter_event_project_id(event_type, raw_payload)
if project_id:
# Fetch All Unpaid Invoice
unpaid_invoice = Invoice.objects.filter(project__tenant_id=project_id, state=Invoice.InvoiceState.UNPAID).first()
used_config = {}
used_day = 0
if unpaid_invoice:
# Calculate Days
# Find last config that has beed runned in past days
date_diff = timezone.now() - unpaid_invoice.end_date
past_day = date_diff.days
for config in schedule_config:
if config['action'] not in self.filter_action:
continue
if config['day'] <= past_day and config['day'] > used_day:
used_day = config['day']
used_config = config
if used_config:
command.run_action(unpaid_invoice, used_config['action'], config)
except Exception:
LOG.exception("Failed to process Unpaid Invoice Event")

View file

@ -4,89 +4,18 @@ import traceback
import openstack
from django.core.management import BaseCommand
from django.utils import timezone
from core.feature.unpaid_invoice_handle.command import UnpaidInvoiceHandlerCommand
from core.models import Invoice
from core.notification import send_notification
from core.utils.dynamic_setting import get_dynamic_setting, BILLING_ENABLED
from yuyu import settings
LOG = logging.getLogger("yuyu_unpaid_invoice_handler")
LOG = logging.getLogger("yuyu")
class Command(BaseCommand):
help = 'Yuyu Unpaid Invoice Handler'
unpaid_invoice_command = UnpaidInvoiceHandlerCommand()
def handle(self, *args, **options):
print("Processing Unpaid Invoice")
# Initialize connection
if not get_dynamic_setting(BILLING_ENABLED):
return
schedule_config = settings.UNPAID_INVOICE_HANDLER_CONFIG
expired_unpaid_invoices = Invoice.objects.filter(state=Invoice.InvoiceState.UNPAID).all()
for invoice in expired_unpaid_invoices:
date_diff = timezone.now() - invoice.end_date
past_day = date_diff.days
for config in schedule_config:
if config['day'] == past_day:
try:
if config['action'] == 'send_message':
self._send_message(invoice, config)
if config['action'] == 'stop_instance':
self._stop_component(invoice)
if config['action'] == 'delete_instance':
self._delete_component(invoice)
except Exception:
send_notification(
project=None,
title='[Error] Error when processing unpaid invoice',
short_description=f'There is an error when processing unpaid invoice',
content=f'There is an error when handling unpaid invoice \n {traceback.format_exc()}',
)
print("Processing Done")
def _stop_component(self, invoice: Invoice):
conn = openstack.connect(cloud=settings.CLOUD_CONFIG_NAME)
# Stop Instance
for server in conn.compute.servers(project_id=invoice.project.tenant_id):
conn.compute.stop_server(server)
def _delete_component(self, invoice: Invoice):
conn = openstack.connect(cloud=settings.CLOUD_CONFIG_NAME)
# Delete Instance
for server in conn.compute.servers(project_id=invoice.project.tenant_id):
conn.compute.delete_server(server)
# Delete Image
for image in conn.compute.images(project_id=invoice.project.tenant_id):
conn.compute.delete_image(image)
# Delete Floating Ips
for ip in conn.network.ips(project_id=invoice.project.tenant_id):
conn.network.delete_ip(ip)
# Delete Router
for route in conn.network.routers(project_id=invoice.project.tenant_id):
conn.network.delete_router(route)
# Delete Volume
for volume in conn.block_storage.volumes(project_id=invoice.project.tenant_id):
conn.block_storage.delete_volume(volume)
# Delete Snapshot
for snapshot in conn.block_storage.snapshots(project_id=invoice.project.tenant_id):
conn.block_storage.delete_snapshot(snapshot)
def _send_message(self, invoice: Invoice, message_config):
print('Sending Message')
send_notification(
project=invoice.project,
title=message_config['message_title'],
short_description=message_config['message_short_description'],
content=message_config['message_content'],
)
self.unpaid_invoice_command.handle()