clean_stale.py 5.11 KB
Newer Older
1 2
#!/usr/bin/env python
from __future__ import absolute_import, division, print_function, unicode_literals
3
import argparse
4
from nova_router import neighbors, security_groups, dns_rr, setupLogfile, etcd_connect
5 6
from scvmm_client import odata
import logging
7
import uuid
8
try:
Sigmund Augdal's avatar
Sigmund Augdal committed
9
    from configparser import SafeConfigParser  # pylint: disable=F0401
10
except ImportError:
Sigmund Augdal's avatar
Sigmund Augdal committed
11
    from ConfigParser import SafeConfigParser  # pylint: disable=F0401
12
import sys
13
import time
14

15 16 17 18 19
DESCRIPTION = """
Remove ip/mac bindings for VMs that no longer exists.
Also remove mac-addresses from security groups in etcd.
Also reset PTR-records in DNS.
"""
20 21 22 23 24 25


def parse_args():
    parser = argparse.ArgumentParser(description=DESCRIPTION)
    parser.add_argument('--logfile', help="send output to logfile")
    parser.add_argument('--config', default="nova.cfg", help="Use alternative config file")
Sigmund Augdal's avatar
Sigmund Augdal committed
26 27
    parser.add_argument('--noop', action="store_true",
                        help="Don't actually clean nodes, just tell what would have been done")
28 29 30

    return parser.parse_args()

31 32

def main():
33
    args = parse_args()
34
    conf = SafeConfigParser()
35 36
    conf.read(args.config)
    if args.logfile:
37
        setupLogfile(args.logfile)
Sigmund Augdal's avatar
Sigmund Augdal committed
38 39
        logging.getLogger("").setLevel(logging.INFO)
        logging.getLogger("urllib3").setLevel(logging.WARNING)
40
        logging.captureWarnings(True)
41 42 43 44
    else:
        logging.basicConfig(level=logging.INFO,
                            format='%(asctime)s %(name)s %(levelname)s %(message)s')

45
    logging.info("Process starting")
46 47 48
    etcd_client = etcd_connect("nova-gw1.bs.unsi.no", conf.get('etcd', 'cert'),
                               conf.get('etcd', 'key'),
                               conf.get('etcd', 'cacert'))
49 50
    vmm_client = odata.VMMClient(conf.get('spf', 'vmm_baseurl'), conf.get('spf', 'username'),
                                 conf.get('spf', 'password'))
51
    dns = dns_rr.client_from_conf(conf)
52 53

    nics = set()
54 55 56 57 58 59 60
    for vm in vmm_client.query_collection("VirtualMachines"):
        vm_nics = vm.get_resource("VirtualNetworkAdapters")
        for nic in vm_nics:
            mac = nic.properties["MACAddress"]
            vmnet = nic.properties["VMNetworkId"]
            if mac is not None and (vmnet == uuid.UUID("5cc21a4c-a5b9-413d-b607-3ce7020c8b98") or vmnet is None):
                nics.add(mac.lower())
61
    for entry in etcd_client.read("/nova/iaas/instances").children:
62 63 64 65
        if not entry.dir:
            next
        mac = entry.key.split("/")[-1].lower()
        if not mac in nics:
66 67 68 69 70 71 72 73
            if not args.noop:
                neighbors.remove_mac(etcd_client, mac)
            for addrtype in (neighbors.V4, neighbors.V6_LL, neighbors.V6_PUB):
                ipaddr = neighbors.get_ipaddress_from_mac(etcd_client, mac, addrtype)
                if ipaddr is not None:
                    if not args.noop:
                        if addrtype == neighbors.V6_LL:
                            continue
74
                        dns.reset_ptr(ipaddr, addrtype != neighbors.V4)
75 76 77 78 79 80
                    else:
                        logging.info("Would have removed address {} from mac {}".format(ipaddr, mac))
                        if addrtype == neighbors.V6_LL:
                            continue
                        if addrtype == neighbors.V6_PUB:
                            logging.info("Would have cleaned DNS PTR records for address {}".format(ipaddr))
81
                            ptr_record = dns.get_ptr_record(ipaddr, addrtype != neighbors.V4)
82 83
                            if ptr_record:
                                logging.info("Would have cleaned record: {}".format(ptr_record))
84
                        else:
85 86
                            # Remove DNS PTR record
                            logging.info("Resetting DNS PTR record for address {}".format(ipaddr))
87
                            ptr_record = dns.get_ptr_record(ipaddr, addrtype != neighbors.V4)
88 89
                            logging.info("Would have updates record: {}".format(ptr_record))
                            if ptr_record:
90
                                ptr_record['content'] = dns_rr.generate_ptr(ipaddr)
91
                                logging.info("New name: {}".format(ptr_record['content']))
92 93 94
        else:
            logging.debug("found owner for mac {}".format(mac))

95 96 97 98 99 100 101 102 103 104 105
    # Remove from security groups
    groups = security_groups.get_security_groups(etcd_client)
    for group in groups:
        _, members = security_groups.get_group_members(etcd_client, group)
        for mac in members:
            if not mac in nics:
                if not args.noop:
                    logging.info("Removing mac {} from security group with id {}".format(mac, group))
                    security_groups.delete_mac_from_group(etcd_client, group, mac)
                else:
                    logging.info("Would have removed mac {} from group with id {}".format(mac, group))
106 107 108
    if not args.noop:
        with open("/var/lib/nova-router/last-successful-clean", "w") as fh:
            fh.write("{}".format(int(time.time())))
109
    logging.info("Process completed")
110 111


112
if __name__ == '__main__':
Sigmund Augdal's avatar
Sigmund Augdal committed
113 114 115 116
    try:
        main()
    except Exception:
        logging.exception("Unhandled exception")
117
        sys.exit(1)