Add cleanup action

We can use fuel-devops for deletion of outdated environment.
Usage:
  dos.py show-old 3d
  dos.py erase-old 3d
where "3d" is time-to-live interval. All envs older than
given interval will be erased.

Last char of interval is multiplier, could be:
 - s: seconds
 - m: minutes
 - h: hours
 - d: days

erase-old operation is interactive but it could be
disabled via "--force-cleanup" arg.

list-old command supports --timestamps arg. Note that devops uses
UTC timestamps!

Change-Id: Ic1a744996495296e22ae0fdef9752dba8790aac8
This commit is contained in:
Vladimir Khlyunev 2017-09-04 11:14:31 +04:00
parent 887368db3b
commit 3ac4e8fc7b
1 changed files with 133 additions and 3 deletions

View File

@ -19,8 +19,11 @@ import collections
import os import os
import sys import sys
import datetime
import tabulate import tabulate
from six.moves import input
import devops import devops
from devops import client from devops import client
from devops import error from devops import error
@ -53,10 +56,53 @@ class Shell(object):
print(tabulate.tabulate(columns, headers=headers, print(tabulate.tabulate(columns, headers=headers,
tablefmt="simple")) tablefmt="simple"))
def do_list(self): @staticmethod
env_names = self.client.list_env_names() def query_yes_no(question, default=None):
"""Ask a yes/no question via standard input and return the answer.
If invalid input is given, the user will be asked until
they acutally give valid input.
Args:
question(str):
A question that is presented to the user.
default(bool|None):
The default value when enter is pressed with no value.
When None, there is no default value and the query
will loop.
Returns:
A bool indicating whether user has entered yes or no.
Side Effects:
Blocks program execution until valid input(y/n) is given.
"""
yes_list = ["yes", "y"]
no_list = ["no", "n"]
default_dict = { # default => prompt default string
None: "[y/n]",
True: "[Y/n]",
False: "[y/N]",
}
default_str = default_dict[default]
prompt_str = "{} {} ".format(question, default_str)
while True:
choice = input(prompt_str).lower()
if not choice and default is not None:
return default
if choice in yes_list:
return True
if choice in no_list:
return False
notification_str = "Please respond with 'y' or 'n'"
print(notification_str)
def print_envs_table(self, env_names_list):
columns = [] columns = []
for env_name in sorted(env_names): for env_name in sorted(env_names_list):
env = self.client.get_env(env_name) env = self.client.get_env(env_name)
column = collections.OrderedDict() column = collections.OrderedDict()
column['NAME'] = env.name column['NAME'] = env.name
@ -72,6 +118,9 @@ class Shell(object):
self.print_table(headers='keys', columns=columns) self.print_table(headers='keys', columns=columns)
def do_list(self):
self.print_envs_table(self.client.list_env_names())
def do_show(self): def do_show(self):
nodes = sorted(self.env.get_nodes(), key=lambda node: node.name) nodes = sorted(self.env.get_nodes(), key=lambda node: node.name)
headers = ("VNC", "NODE-NAME", "GROUP-NAME") headers = ("VNC", "NODE-NAME", "GROUP-NAME")
@ -131,6 +180,58 @@ class Shell(object):
def do_erase(self): def do_erase(self):
self.env.erase() self.env.erase()
def get_lifetime_delta(self):
data = self.params.env_lifetime
multipliers = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}
if data[-1] not in multipliers:
raise ValueError(
'Value should end with '
'one of "{}", got "{}"'.format(
" ".join(multipliers.keys()), data
))
num = int(data[:-1])
mul = data[-1]
return datetime.timedelta(seconds=num*multipliers[mul])
def get_old_environments(self):
delta = self.get_lifetime_delta()
# devops uses utc timestamps for BaseModel
timestamp_now = datetime.datetime.utcnow()
envs_to_erase = []
for env_name in client.DevopsClient.list_env_names():
env = client.DevopsClient.get_env(env_name)
if (timestamp_now - env.created) > delta:
envs_to_erase.append(env)
return envs_to_erase
def do_erase_old(self):
envs_to_erase = self.get_old_environments()
for env in envs_to_erase:
print("Env '{}' will be erased!".format(env.name))
if envs_to_erase:
if not self.params.force_cleanup:
answer = self.query_yes_no(
"The cleanup operation is destructive one, "
"all environments listed above will be erased. "
"DELETION CAN NOT BE UNDONE! Proceed? ",
default=False)
if not answer:
print("Wise choice, aborting...")
sys.exit(0)
else:
print("Nothing to erase, exiting...")
sys.exit(0)
for env in envs_to_erase:
print("Erasing '{}'...".format(env.name))
env.erase()
def do_list_old(self):
env_names = [env.name for env in self.get_old_environments()]
self.print_envs_table(env_names)
def do_start(self): def do_start(self):
self.env.start() self.env.start()
@ -475,6 +576,22 @@ class Shell(object):
'If set to 0, the disk will not be ' 'If set to 0, the disk will not be '
'allocated', 'allocated',
default=50, type=int) default=50, type=int)
force_cleanup_parser = argparse.ArgumentParser(add_help=False)
force_cleanup_parser.add_argument(
'--force-cleanup',
dest='force_cleanup',
action='store_const', const=True,
help='Do not ask confirmation for cleanup action.',
default=False)
env_lifetime = argparse.ArgumentParser(add_help=False)
env_lifetime.add_argument(
dest='env_lifetime',
help='Erase environments older than given time interval. '
'Example:"45m", "12h", "3d"',
default="", type=str)
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Manage virtual environments. " description="Manage virtual environments. "
"For additional help, use with -h/--help option") "For additional help, use with -h/--help option")
@ -485,6 +602,19 @@ class Shell(object):
parents=[list_ips_parser, timestamps_parser], parents=[list_ips_parser, timestamps_parser],
help="Show virtual environments", help="Show virtual environments",
description="Show virtual environments on host") description="Show virtual environments on host")
subparsers.add_parser('erase-old',
parents=[force_cleanup_parser,
env_lifetime],
help="Cleanup old virtual environments",
description="Cleanup virtual environments on "
"host")
subparsers.add_parser('list-old',
parents=[env_lifetime, list_ips_parser,
timestamps_parser],
help="Show virtual environments older than given"
" lifetime interval",
description="Show old virtual "
"environments on host")
subparsers.add_parser('show', parents=[name_parser], subparsers.add_parser('show', parents=[name_parser],
help="Show VMs in environment", help="Show VMs in environment",
description="Show VMs in environment") description="Show VMs in environment")