112 lines
4.3 KiB
Python
112 lines
4.3 KiB
Python
from collections import defaultdict
|
|
import datetime
|
|
import json
|
|
import os
|
|
from typing import Dict
|
|
|
|
from zope import component
|
|
from zope.interface import implementer
|
|
|
|
from ceo_common.logger_factory import logger_factory
|
|
from ceo_common.interfaces import ICloudResourceManager, \
|
|
ILDAPService, IMailService, IKubernetesService, IVHostManager, \
|
|
ICloudStackService
|
|
from ceo_common.model import Term
|
|
import ceo_common.utils as utils
|
|
|
|
logger = logger_factory(__name__)
|
|
|
|
|
|
@implementer(ICloudResourceManager)
|
|
class CloudResourceManager:
|
|
def __init__(self):
|
|
state_dir = '/run/ceod'
|
|
if not os.path.isdir(state_dir):
|
|
os.mkdir(state_dir)
|
|
self.pending_deletions_file = \
|
|
os.path.join(state_dir, 'pending_account_deletions.json')
|
|
|
|
def purge_accounts(self) -> Dict:
|
|
accounts_deleted = []
|
|
accounts_to_be_deleted = []
|
|
result = {
|
|
'accounts_deleted': accounts_deleted,
|
|
'accounts_to_be_deleted': accounts_to_be_deleted,
|
|
}
|
|
|
|
current_term = Term.current()
|
|
beginning_of_term = current_term.to_datetime()
|
|
now = utils.get_current_datetime()
|
|
delta = now - beginning_of_term
|
|
if delta.days < 30:
|
|
# one-month grace period
|
|
return result
|
|
|
|
ldap_srv = component.getUtility(ILDAPService)
|
|
mail_srv = component.getUtility(IMailService)
|
|
k8s_srv = component.getUtility(IKubernetesService)
|
|
vhost_mgr = component.getUtility(IVHostManager)
|
|
cloudstack_srv = component.getUtility(ICloudStackService)
|
|
|
|
# get a list of all cloud services each user is using
|
|
accounts = defaultdict(list)
|
|
cloudstack_accounts = cloudstack_srv.get_accounts()
|
|
# note that cloudstack_accounts is a dict, not a list
|
|
for username in cloudstack_accounts:
|
|
accounts[username].append('cloudstack')
|
|
vhost_accounts = vhost_mgr.get_accounts()
|
|
for username in vhost_accounts:
|
|
accounts[username].append('vhost')
|
|
k8s_accounts = k8s_srv.get_accounts()
|
|
for username in k8s_accounts:
|
|
accounts[username].append('k8s')
|
|
|
|
if os.path.isfile(self.pending_deletions_file):
|
|
state = json.load(open(self.pending_deletions_file))
|
|
last_check = datetime.datetime.fromtimestamp(state['timestamp'])
|
|
delta = now - last_check
|
|
if delta.days < 7:
|
|
logger.debug(
|
|
'Skipping account purge because less than one week has '
|
|
'passed since the warning emails were sent out'
|
|
)
|
|
accounts_to_be_deleted.extend(state['accounts_to_be_deleted'])
|
|
return result
|
|
for username in state['accounts_to_be_deleted']:
|
|
if username not in accounts:
|
|
continue
|
|
user = ldap_srv.get_user(username)
|
|
if user.membership_is_valid():
|
|
continue
|
|
services = accounts[username]
|
|
if 'cloudstack' in services:
|
|
account_id = cloudstack_accounts[username]
|
|
cloudstack_srv.delete_account(account_id)
|
|
if 'vhost' in services:
|
|
vhost_mgr.delete_all_vhosts_for_user(username)
|
|
if 'k8s' in services:
|
|
k8s_srv.delete_account(username)
|
|
accounts_deleted.append(username)
|
|
mail_srv.send_cloud_account_has_been_deleted_message(user)
|
|
logger.info(f'Deleted cloud resources for {username}')
|
|
os.unlink(self.pending_deletions_file)
|
|
return result
|
|
|
|
state = {
|
|
'timestamp': int(now.timestamp()),
|
|
'accounts_to_be_deleted': accounts_to_be_deleted,
|
|
}
|
|
for username in accounts:
|
|
user = ldap_srv.get_user(username)
|
|
if user.membership_is_valid():
|
|
continue
|
|
accounts_to_be_deleted.append(username)
|
|
mail_srv.send_cloud_account_will_be_deleted_message(user)
|
|
logger.info(
|
|
f'A warning email was sent to {username} because their '
|
|
'cloud account will be deleted'
|
|
)
|
|
if accounts_to_be_deleted:
|
|
json.dump(state, open(self.pending_deletions_file, 'w'))
|
|
return result
|