]> review.fuel-infra Code Review - tools/sustaining.git/commitdiff
Add keypair cleanup feature 62/41762/13
authorVladimir Khlyunev <vkhlyunev@mirantis.com>
Mon, 19 Jul 2021 23:10:31 +0000 (03:10 +0400)
committerVladimir Khlyunev <vkhlyunev@mirantis.com>
Thu, 29 Jul 2021 10:52:13 +0000 (14:52 +0400)
./shell.py cleanup keypair --lifetime 60d --name testname

Change-Id: I4c911dd49d11911c67540751bd410a5883e753ea

os_cloud_cleaner/cleaner.py
os_cloud_cleaner/helpers.py
os_cloud_cleaner/ignorelist.txt
os_cloud_cleaner/keypair_keeplist.yml [new file with mode: 0644]
os_cloud_cleaner/os_connector.py
os_cloud_cleaner/shell.py

index 8b612061f8109b96f280a79b4b68195fd40d5a4e..2f76cc09dd97959e97256f5df68eb5f7c0041973 100644 (file)
@@ -2,11 +2,16 @@ from __future__ import unicode_literals
 
 import os
 from collections import OrderedDict
-from datetime import datetime
+import datetime
+
+import yaml
+from dateutil import parser as date_parser
 
 from os_connector import OpenStackActions
 from logger import logger
 
+from helpers import get_lifetime_delta
+
 try:
     import prehooks
 except ImportError:
@@ -23,12 +28,18 @@ class Cleaner:
                  ignorelist_file=None):
 
         self.ignorelist_uuids = []
-        if ignorelist_file is not None and os.path.exists(ignorelist_file):
-            with open(ignorelist_file) as f:
-                for line in f.readlines():
-                    if line and not line.startswith("#"):
-                        self.ignorelist_uuids.append(line.split(" ")[0])
+        if ignorelist_file is not None:
+            if os.path.exists(ignorelist_file):
+                with open(ignorelist_file) as f:
+                    for line in f.readlines():
+                        if line and not line.startswith("#"):
+                            self.ignorelist_uuids.append(line.split(" ")[0])
+            else:
+                logger.error("ERROR: ignorelist file is passed but not found! "
+                             "(could be working dir issue")
+                raise AttributeError
 
+        self.os_user = os_user
         self.os_conn = OpenStackActions(
             auth_url=os_auth_url,
             user=os_user,
@@ -187,6 +198,9 @@ class Cleaner:
                 'routers': routers,
                 'subnets': subnets}
 
+    def get_keypairs(self):
+        return self.os_conn.get_keypairs()
+
     def get_servers(self):
         return self.os_conn.get_servers()
 
@@ -261,3 +275,56 @@ class Cleaner:
             self.cleanup_stack_parallel(uuid)
         else:
             logger.info("Can't find anything with is {}".format(uuid))
+
+    def get_user_keypair_keeplist(self, raise_exc=True):
+        file_exists = os.path.exists('keypair_keeplist.yml')
+        if not file_exists:
+            raise AssertionError("keypair_keeplist not found, "
+                                 "too dangerous to proceed")
+        with open("keypair_keeplist.yml") as f:
+            keeplist = yaml.safe_load(f)
+        current_user_keeplist = keeplist.get(self.os_user, [])
+        if not current_user_keeplist:
+            if raise_exc:
+                raise AssertionError(
+                    "keeplist for user {} empty, too dangerous "
+                    "for cleanup operation".format(self.os_user))
+            else:
+                logger.warning("keeplist for user {} empty; cleanup operation "
+                               "will be unavailable".format(self.os_user))
+        return current_user_keeplist
+
+    def search_keypairs(self, name='', fingerprint='', lifetime=''):
+        current_user_keeplist = self.get_user_keypair_keeplist(raise_exc=False)
+
+        keypairs = self.get_keypairs()
+        result = []
+        now = datetime.datetime.utcnow()
+
+        def check_gen_req(name, keypair_name):
+            if name == "DETECT_128":
+                return len(keypair_name) == 128
+            return name in keypair_name
+
+        for keypair in keypairs:
+            if keypair.name in current_user_keeplist:
+                logger.info("IGNORE > keypair {}".format(keypair.name))
+                continue
+            if check_gen_req(name,
+                             keypair.name) and fingerprint in keypair.fingerprint:
+                if lifetime:
+                    lifetime_delta = get_lifetime_delta(lifetime)
+                    if now - date_parser.parse(
+                            keypair.created_at) > lifetime_delta:
+                        result.append(keypair)
+                else:
+                    result.append(keypair)
+        return result
+
+    def cleanup_keypairs(self, name='', fingerprint='', lifetime=''):
+        keypairs_to_delete = self.search_keypairs(name, fingerprint, lifetime)
+        # use get user keypair keeplist to stop script if there is no keeplist
+        self.get_user_keypair_keeplist(raise_exc=True)
+        for keypair in keypairs_to_delete:
+            logger.info("Deleting keypair {}".format(keypair.name))
+            keypair.delete()
index d209d635b8a088a6fb81a9eea7614cfff08ba55e..4caeecab786e84b509521159cf19fe9f6d1ce40a 100644 (file)
@@ -1,3 +1,4 @@
+import datetime
 import signal
 import time
 
@@ -125,3 +126,16 @@ def _check_wait_args(predicate,
                                         predicate_args,
                                         predicate_kwargs,
                                         timeout))
+
+
+def get_lifetime_delta(lifetime):
+    multipliers = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}
+    if lifetime[-1] not in multipliers:
+        raise ValueError(
+            'Value should end with '
+            'one of "{}", got "{}"'.format(
+                " ".join(multipliers.keys()), lifetime
+            ))
+    num = int(lifetime[:-1])
+    mul = lifetime[-1]
+    return datetime.timedelta(seconds=num*multipliers[mul])
index a1b46a4d9c590a6e0fbb517aec460cd15faf3bd7..2d8aea821137aca40395e6c4b23aa76e3c1275c2 100644 (file)
@@ -6,6 +6,9 @@ ba308971-abd7-4630-a1f1-06d2caa3be37 # dstremkouski-k8s-node01
 3fb4af2b-40d4-4ec3-88a0-17c3edd8ed67 # dstremkouski-k8s-node02
 1589214e-43bf-4d57-80ac-68b28030c536 # sre-monitoring
 5b222dd9-aeb9-42bd-8fac-e8f3874238d9 # dench-desktop
+d6c676ff-d47d-4d64-8624-977b4d1ff27b # sre-team-infra
+64b25fd1-d59f-4eb0-a732-cc500fd87ba1 # pkgs-ci-xenial-slave01
+a680c184-60f0-4ac8-9c0d-a6cc7a083bb1 # model-manager-prod
 # Networks
 0563f790-92a3-4c92-ab41-74478442a75e # physnet1-402
 b7b8b2e1-fb2e-4edb-9540-500363155fe3 # cvp-internal
@@ -23,9 +26,16 @@ e977057f-ab64-466f-b953-1d9e44ac9f36 # system-phys-2404
 5d4a2029-6c32-4bee-89dc-855829cc2233 # physnet1-432
 c11bf78a-de07-4e67-8047-a28b247c27bb # sre-internal
 bf6b85a1-39db-4582-b0d1-f4291dddb9cf # public
+07545b24-decb-4b19-848e-30937aa85359 # sre-team-default-internal-net
+30e7b0b7-4fab-4dbb-bc38-4c64d489d3df # pkgs-ci-xenial-slave-net
+c3799996-dc8e-4477-a309-09ea6dd71946 # public
+af8a9bba-627f-4b79-9416-8595897cb5e2 # model-manager-net
 # Routers
 d92fc584-371b-4c0b-aa85-9c5c49292129 # sre-router
 808a47c2-7ee4-46c2-b232-b625c70bd5d7 # vdrok-unittest
+1c7803d2-0fcc-4084-9a89-e4b133270a77 # model-manager-router
+4b78d573-2435-429b-8e9d-5f4ca88f2271 # pkgs-ci-xenial-slave-router
+49851843-4a8a-411b-8d62-c47e536f9902 # sre-team-default-router
 # Volumes
 83bada1a-3a04-413f-b3ef-d0f26adf69e2 # dstremkouski-convert-qcow2-raw
 0d0665c2-c684-4a66-a224-72462ccb016d # dstremkouski-500G-stripe
@@ -34,7 +44,12 @@ da6ff39c-01cc-4323-9c76-ab7fc243fae4 # dstremkouski-jumphost-eu
 d0eb9a28-8ea1-4728-9fcc-5e89d1fd773d # dstremkouski-500G
 d32349f4-74eb-4acb-a7a4-b9ae32029000 # windows-vw-jump
 48a15d0c-5c48-45dc-b8db-13462239278c # sre-monitoring
+5eb4a3c6-4dc3-437e-9ac5-0b15a1ffe873 # pkgs-ci-xenial-slave01 as /dev/vda
+36a7a3a0-0c0e-4999-a793-d95003025100 # model-manager-prod-11092019
 # Subnets
+04999e63-a03f-4916-9923-01eb2e71a362 # model-manager-subnet
+a5f29252-f0c0-4bff-887a-44217bd8c151 # pkgs-ci-xenial-slave-subnet
+d83983b6-16b6-4a72-a6df-71583bbd1f88 # sre-team-default-internal-subnet
 4e248805-d684-4411-bb3f-143ec8da5bad # cvp-subnet-internal-2
 7d7e65ba-d6cf-458f-b963-50614fac5e00 # baremetal-subnet
 0822b2c1-4676-46f1-a382-54d3c9ab1458 # physnet1-432-subnet
diff --git a/os_cloud_cleaner/keypair_keeplist.yml b/os_cloud_cleaner/keypair_keeplist.yml
new file mode 100644 (file)
index 0000000..a77fbb5
--- /dev/null
@@ -0,0 +1,20 @@
+# mentioned here keypairs will never be deleted by cleaner; add your keys if needed here
+# username:
+#    - keypair_name
+#    - keypair_name
+vkhlyunev:
+  - jenkins-patching-ci # US
+  - vkhlyunev # US,EU
+maintenance-ci-robot:
+  - maintenance-ci-public-key
+  - system-key-8133
+drivetrain-jenkins:
+  - oscore-devcloud-env-ssh-public
+  - jenkins-mk
+  - k8s-helm-aio
+  - mcp-scale-jenkins
+  - mcpng2cmp
+  - system-key-8133
+  - system_key_8133
+  - mosk-ga
+  - scale-team-heat-mcp-2
\ No newline at end of file
index 2b7d5c6e0bc584a34ae55b34638758980e734ce8..89420334741119dc7d43c375b5597a086ed6a6a1 100644 (file)
@@ -1,7 +1,5 @@
 from __future__ import unicode_literals
 
-import time
-
 from cinderclient.exceptions import NotFound
 from cinderclient.v2.client import Client as CinderClient
 from heatclient.v1.client import Client as HeatClient
@@ -117,6 +115,16 @@ class OpenStackActions(object):
             if f_ip_data['floating_ip_address'] == floating_ip:
                 return f_ip_data['id']
 
+    def get_keypairs(self):
+        keypairs = self.nova.keypairs.list()
+        resp = []
+        for keypair in keypairs:
+            resp.append(self.nova.keypairs.get(keypair.name))
+        return resp
+
+    def get_one_keypair(self, name):
+        return self.nova.keypairs.get(name)
+
     def get_ports(self):
         response = self.neutron.list_ports()
         ports = response['ports']
@@ -190,12 +198,6 @@ class OpenStackActions(object):
             return True
         return False
 
-    def check_subnet_exists(self, subnet_uuid):
-        resp = self.neutron.list_subnets(id=subnet_uuid)['subnets']
-        if resp:
-            return True
-        return False
-
     def check_any_network_exists(self, uuids):
         return any(
             [self.check_network_exists(uuid) for uuid in uuids]
index 85d78564b709857aa137f930069551950ec6214a..c80b7e64de70da6b90e50d40b9b50b638a35977a 100644 (file)
@@ -1,11 +1,14 @@
 import argparse
+from dateutil import parser as date_parser
 import os
 import sys
+
 from prettytable import PrettyTable
 
 from cleaner import Cleaner
 from logger import logger
 
+
 try:
     import prehooks
 except ImportError:
@@ -37,6 +40,16 @@ def do_search(name, only_uuids):
             print table
 
 
+def do_search_keypair(name='', fingerprint='', lifetime=''):
+    keypairs = cleaner.search_keypairs(name, fingerprint, lifetime)
+    table = PrettyTable()
+    table.field_names = ['name', 'created_at', 'fingerprint']
+    table.align = 'l'
+    for value in sorted(keypairs, reverse=True, key=lambda x: date_parser.parse(x.created_at)):
+        table.add_row([value.name, value.created_at, value.fingerprint])
+    print table
+
+
 parser = argparse.ArgumentParser()
 parser.add_argument('--os-auth-url', type=str)
 parser.add_argument('--os-username', type=str)
@@ -53,15 +66,55 @@ cleanup_subparser.add_argument('resource_type', type=str,
                                help="allowed: stack, misc")
 cleanup_subparser.add_argument('ids', nargs='*', type=str,
                                help="uuids or names, error if name duplicates")
-# cleanup_subparser.add_argument('--force', '-f', default=False,
-#                                action='store_true')
+
+cleanup_subparser.add_argument('--name',
+                               '-n',
+                               type=str,
+                               help='Search resources that only contains given name;'
+                                    'usable for: search non-stack keypair operation; '
+                                    'for keypair there is pre-defined DETECT_128 for '
+                                    '128char generated names',
+                               default='')
+cleanup_subparser.add_argument('--lifetime',
+                               '-l',
+                               type=str,
+                               help='Usable for keypairs only; search/cleanuo keypairs only '
+                                    'older than given timeframe; '
+                                    '30d - older than 30 days',
+                               default='')
+cleanup_subparser.add_argument('--fingerprint',
+                               type=str,
+                               help='Usable for keypairs only; Search resources '
+                                    'that only contains given fingerprint',
+                               default='')
+
 search_subparser = subparsers.add_parser('search')
-search_subparser.add_argument('--name', '-n', type=str,
-                              help='part of name to search for',
-                              default='')
+search_subparser.add_argument('resource_type', type=str,
+                              help="allowed: misc, keypair")
+
 search_subparser.add_argument('--only-uuids', '-i',
                               help='print only uuids for chain xargs call',
                               default=False, action='store_true')
+search_subparser.add_argument('--name',
+                              '-n',
+                              type=str,
+                              help='Search resources that only contains given name;'
+                                   'usable for: search non-stack keypair operation; '
+                                   'for keypair there is pre-defined DETECT_128 for '
+                                   '128char generated names',
+                              default='')
+search_subparser.add_argument('--lifetime',
+                              '-l',
+                              type=str,
+                              help='Usable for keypairs only; search/cleanuo keypairs only '
+                                   'older than given timeframe; '
+                                   '30d - older than 30 days',
+                              default='')
+search_subparser.add_argument('--fingerprint',
+                              type=str,
+                              help='Usable for keypairs only; Search resources '
+                                   'that only contains given fingerprint',
+                              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(" "))
@@ -70,7 +123,10 @@ search_subparser.add_argument('--only-uuids', '-i',
 # args = parser.parse_args("search".split(" "))
 
 # debug_args = "cleanup stack heat-cicd-queens-dvr-sl"
-# debug_args = "search -n vkhlyunev"
+# 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"
 # args = parser.parse_args(debug_args.split(" "))
 args = parser.parse_args()
 
@@ -85,9 +141,9 @@ auth_data['user_domain_name'] = args.os_user_domain_name or os.environ.get(
     'OS_USER_DOMAIN_NAME')
 
 for validated_item in auth_data:
-  if not auth_data[validated_item]:
-    logger.error("Parameter {} NOT defined.".format(validated_item))
-    sys.exit(124)
+    if not auth_data[validated_item]:
+        logger.error("Parameter {} NOT defined.".format(validated_item))
+        sys.exit(124)
 
 cleaner = Cleaner(os_auth_url=auth_data['auth_url'],
                   os_user=auth_data['username'],
@@ -116,7 +172,7 @@ if args.action == "cleanup":
                         os_id))
             else:
                 logger.info("Stack not found, nothing to delete")
-    if args.resource_type == "misc":
+    elif args.resource_type == "misc":
         os_ids = args.ids
         if not os_ids:
             data = sys.stdin.read()
@@ -125,5 +181,12 @@ if args.action == "cleanup":
             if os_id:
                 logger.info("Processing {}...".format(os_id))
                 cleaner.process_resource(os_id)
+    elif args.resource_type == "keypair":
+        cleaner.cleanup_keypairs(args.name, args.fingerprint, args.lifetime)
+
 elif args.action == "search":
-    do_search(args.name, args.only_uuids)
+    if args.resource_type == "non-stack":
+        do_search(args.name, args.only_uuids)
+    elif args.resource_type == "keypair":
+        do_search_keypair(args.name, args.fingerprint, args.lifetime)
+