From 739d58b18619590d8ba2f2e6a22b38dc65e5806f Mon Sep 17 00:00:00 2001 From: Vladimir Khlyunev Date: Thu, 7 Oct 2021 14:46:34 +0400 Subject: [PATCH] Add stack resources calculation feature Change-Id: I18b6a8589449c8f32b001b4938f2708781a2e91c --- os_cloud_cleaner/cleaner.py | 86 +++++++++++++++++++++----------- os_cloud_cleaner/os_connector.py | 13 +++++ os_cloud_cleaner/shell.py | 27 +++++++++- 3 files changed, 95 insertions(+), 31 deletions(-) diff --git a/os_cloud_cleaner/cleaner.py b/os_cloud_cleaner/cleaner.py index 2f76cc0..8531ed0 100644 --- a/os_cloud_cleaner/cleaner.py +++ b/os_cloud_cleaner/cleaner.py @@ -216,45 +216,48 @@ class Cleaner: def get_volumes(self): return self.os_conn.get_volumes() - def cleanup_stack_parallel(self, stack): - if not hasattr(stack, 'id'): - stack = list(self.os_conn.heat.stacks.list(id=stack)) - if not stack: - logger.info("Can not find stack {}".format(stack)) - stack = stack[0] - + def get_stack_resources_split(self, stack): resources = self.get_os_stack_resources(stack) - server_uuids = [] - router_uuids = [] - network_uuids = [] - subnets_uuids = [] - secgroup_uuids = [] - floating_ip_uuids = [] - volumes_uuids = [] - temp = set() + result = { + 'server_uuids':[], + 'router_uuids':[], + 'network_uuids':[], + 'subnets_uuids':[], + 'secgroup_uuids':[], + 'floating_ip_uuids':[], + 'volumes_uuids':[], + } for uuid, resource in resources.items(): - temp.add(resource["resource_type"]) if resource["resource_type"] == "OS::Nova::Server": - server_uuids.append(uuid) + result['server_uuids'].append(uuid) elif resource["resource_type"] == "OS::Neutron::Router": - router_uuids.append(uuid) + result['router_uuids'].append(uuid) elif resource["resource_type"] == "OS::Neutron::Net": - network_uuids.append(uuid) + result['network_uuids'].append(uuid) elif resource["resource_type"] == "OS::Neutron::Subnet": - subnets_uuids.append(uuid) + result['subnets_uuids'].append(uuid) elif resource["resource_type"] == "OS::Neutron::SecurityGroup": - secgroup_uuids.append(uuid) + result['secgroup_uuids'].append(uuid) elif resource["resource_type"] == "OS::Neutron::FloatingIP": - floating_ip_uuids.append(uuid) + result['floating_ip_uuids'].append(uuid) elif resource["resource_type"] == "OS::Cinder::Volume": - volumes_uuids.append(uuid) - - self.os_conn.cleanup_floating_batch(floating_ip_uuids) - self.os_conn.cleanup_servers_batch(server_uuids) - self.os_conn.cleanup_volumes_batch(volumes_uuids) - self.os_conn.cleanup_router_batch(router_uuids) - self.os_conn.cleanup_network_batch(network_uuids) - self.os_conn.cleanup_subnet_batch(subnets_uuids) + result['volumes_uuids'].append(uuid) + return result + + def cleanup_stack_parallel(self, stack): + if not hasattr(stack, 'id'): + stack = list(self.os_conn.heat.stacks.list(id=stack)) + if not stack: + logger.info("Can not find stack {}".format(stack)) + stack = stack[0] + + resources = self.get_stack_resources_split(stack) + self.os_conn.cleanup_floating_batch(resources['floating_ip_uuids']) + self.os_conn.cleanup_servers_batch(resources['server_uuids']) + self.os_conn.cleanup_volumes_batch(resources['volumes_uuids']) + self.os_conn.cleanup_router_batch(resources['router_uuids']) + self.os_conn.cleanup_network_batch(resources['network_uuids']) + self.os_conn.cleanup_subnet_batch(resources['subnets_uuids']) self.os_conn.cleanup_stack(stack.id, ignore_err=True) def process_resource(self, uuid): @@ -328,3 +331,26 @@ class Cleaner: for keypair in keypairs_to_delete: logger.info("Deleting keypair {}".format(keypair.name)) keypair.delete() + + def calculate_resources(self, stack): + if isinstance(stack, str): + stack = self.os_conn.get_stacks_by_name_or_uuid(stack)[0] + resources = self.get_stack_resources_split(stack) + result = {'vcpus': 0, 'ram': 0, 'name': stack.stack_name, + 'volumes': 0} + for uuid in resources['server_uuids']: + server = self.os_conn.get_server_by_uuid(uuid) + if not server: + continue + fl_uuid = server.flavor['id'] + flavor = self.os_conn.get_flavor(fl_uuid) + fl_dict = flavor.to_dict() + for key in ['vcpus', 'ram']: + if key in fl_dict: + result[key] += fl_dict[key] + for vol_uuid in resources['volumes_uuids']: + volume = self.os_conn.get_volume(vol_uuid) + if not volume: + continue + result['volumes'] += volume.size + return result \ No newline at end of file diff --git a/os_cloud_cleaner/os_connector.py b/os_cloud_cleaner/os_connector.py index c6c8ce5..cd0871d 100644 --- a/os_cloud_cleaner/os_connector.py +++ b/os_cloud_cleaner/os_connector.py @@ -115,6 +115,12 @@ class OpenStackActions(object): if f_ip_data['floating_ip_address'] == floating_ip: return f_ip_data['id'] + def get_flavor(self, uuid): + return self.nova.flavors.get(uuid) + + def get_volume(self, uuid): + return self.cinder.volumes.get(uuid) + def get_keypairs(self): keypairs = self.nova.keypairs.list() resp = [] @@ -146,6 +152,13 @@ class OpenStackActions(object): except NovaNotFoundException: return None + def get_server_by_uuid(self, uuid): + try: + server = self.nova.servers.get(uuid) + return server + except NovaNotFoundException: + return None + def get_keystone_endpoints(self): endpoints = self.keystone.endpoints.list() return endpoints diff --git a/os_cloud_cleaner/shell.py b/os_cloud_cleaner/shell.py index c80b7e6..511e5f1 100644 --- a/os_cloud_cleaner/shell.py +++ b/os_cloud_cleaner/shell.py @@ -50,6 +50,25 @@ def do_search_keypair(name='', fingerprint='', lifetime=''): print table +def do_calculate(name=''): + table = PrettyTable() + table.field_names = ['stack_name', 'vcpus', 'ram_gb', 'volumes_gb'] + table.align = 'l' + + if name is '': + logger.info("Stack resources calculation initiated without stack name, this will take long time.") + logger.info("Please standby") + stacks = cleaner.get_stacks() + else: + stacks = [name] + + for stack in stacks: + data = cleaner.calculate_resources(stack) + ram_gb = data['ram'] / 1024 + table.add_row([data['name'], data['vcpus'], ram_gb, data['volumes']]) + print table + + parser = argparse.ArgumentParser() parser.add_argument('--os-auth-url', type=str) parser.add_argument('--os-username', type=str) @@ -116,17 +135,21 @@ search_subparser.add_argument('--fingerprint', 'that only contains given fingerprint', default='') +calculate_subparser = subparsers.add_parser('calculate') +calculate_subparser.add_argument('--name', type=str, default='') # args = parser.parse_args("cleanup stack bm-cicd-pike-ovs-maas".split(" ")) # args = parser.parse_args("search -i -n bm-cicd-pike-ovs-maas".split(" ")) # args = parser.parse_args("cleanup misc 6637ecb1-661f-4d6e-a160-b05ee8b81cd0".split(" ")) # args = parser.parse_args("search -i".split(" ")) # args = parser.parse_args("search".split(" ")) -# debug_args = "cleanup stack heat-cicd-queens-dvr-sl" +# debug_args = "cleanup stack pike-ci-dvr-2k19-2" # debug_args = "search non-stack -n vkhlyunev" # debug_args = "search non-stack" # debug_args = "search keypair -l 1d" # debug_args = "cleanup keypair --name test -l 1m" +# debug_args = "calculate --name vkhlyunev-released-queens-2.6" +# debug_args = "calculate" # args = parser.parse_args(debug_args.split(" ")) args = parser.parse_args() @@ -190,3 +213,5 @@ elif args.action == "search": elif args.resource_type == "keypair": do_search_keypair(args.name, args.fingerprint, args.lifetime) +elif args.action == "calculate": + do_calculate(args.name) -- 2.45.2