#!/usr/bin/env python

from ovirtsdk4 import Connection, types
from sys import exit

# Connection parameters
URI = 'https://fqdn/ovirt-engine/api'
USERNAME = 'admin@internal'
PASSWORD = '...'
CERTPATH = '/etc/.../.../...'

# MAC pools in your oVirt installation (it assumes you just write the 5 first octets and the
# last one goes from 00 to ff).
MACPOOLS = ['00:..', '00:..']

# No need to modify anything else beyond this line.
MACOCTETS = ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '0a', '0b', '0c', '0d', '0e', '0f',
             '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1a', '1b', '1c', '1d', '1e', '1f',
             '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '2a', '2b', '2c', '2d', '2e', '2f',
             '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '3a', '3b', '3c', '3d', '3e', '3f',
             '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '4a', '4b', '4c', '4d', '4e', '4f',
             '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '5a', '5b', '5c', '5d', '5e', '5f',
             '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '6a', '6b', '6c', '6d', '6e', '6f',
             '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '7a', '7b', '7c', '7d', '7e', '7f',
             '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '8a', '8b', '8c', '8d', '8e', '8f',
             '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '9a', '9b', '9c', '9d', '9e', '9f',
             'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9', 'aa', 'ab', 'ac', 'ad', 'ae', 'af',
             'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'ba', 'bb', 'bc', 'bd', 'be', 'bf',
             'c0', 'c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9', 'ca', 'cb', 'cc', 'cd', 'ce', 'cf',
             'd0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9', 'da', 'db', 'dc', 'dd', 'de', 'df',
             'e0', 'e1', 'e2', 'e3', 'e4', 'e5', 'e6', 'e7', 'e8', 'e9', 'ea', 'eb', 'ec', 'ed', 'ee', 'ef',
             'f0', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'fa', 'fb', 'fc', 'fd', 'fe', 'ff']

conn = Connection(
   url=URI,
   username=USERNAME,
   password=PASSWORD,
   ca_file=CERTPATH
)

if not conn.test(raise_exception=False):
    print "ERROR: Cannot connect (credencials?)"
    exit(1)

vms_macs = {}

sys_serv = conn.system_service()
vms_serv = sys_serv.vms_service()

macs = []
for vm in vms_serv.list():
    nics = conn.follow_link(vm.nics)
    for nic in nics:
        if nic.mac.address in vms_macs:
            vms_macs[nic.mac.address].append(vm.name)
        else:
            vms_macs[nic.mac.address] = [vm.name]
        macs.append(nic.mac.address)

def find_unused_mac():
    global MACOCTETS, MACPOOLS, macs

    for pool in MACPOOLS:
        for octet in MACOCTETS:
            mac = "%s:%s" % (pool, octet)
            if mac not in macs:
                macs.append(mac)
                return mac
    return None

def assign_new_mac(vm, oldmac, newmac):
    vmnic = None
    nics = conn.follow_link(vm.nics)
    for nic in nics:
        if nic.mac.address == oldmac:
            vmnic = nic

    if not vmnic:
        print "[WARN] Cannot find nic with MAC %s on VM %s" % (oldmac, vm.name)
        return
    
    nics_serv = vms_serv.vm_service(vm.id).nics_service()
    ns = nics_serv.nic_service(id=vmnic.id)
    mac = types.Mac(address=newmac)
    ns.update(
        types.Nic(mac=mac)
    )

def solve_conflict(mac, vms):
    if len(vms) > 2:
        print "[WARN]: There are more than 2 VMs with the same MAC, please solve manually."
        return

    vm1 = vms_serv.list(search='name=%s' % (vms[0]))[0]
    vm2 = vms_serv.list(search='name=%s' % (vms[1]))[0]

    print "VM1 status: %s" % (vm1.status)
    print "VM2 status: %s" % (vm2.status)

    if vm1.status == types.VmStatus.UP and vm2.status == types.VmStatus.UP:
        print "Both VMs are up, please turn one off manually."
    elif vm1.status == types.VmStatus.DOWN and vm2.status == types.VmStatus.DOWN:
        newmac = find_unused_mac()
        print "VM %s is off, assigning MAC %s" % (vm1.name, newmac)
        assign_new_mac(vm1, mac, newmac)
    elif vm1.status == types.VmStatus.DOWN:
        newmac = find_unused_mac()
        print "VM %s is off, assigning MAC %s" % (vm1.name, newmac)
        assign_new_mac(vm1, mac, newmac)
    elif vm2.status == types.VmStatus.DOWN:
        newmac = find_unused_mac()
        print "VM %s is off, assigning MAC %s" % (vm2.name, newmac)
        assign_new_mac(vm2, mac, newmac)
    print "- - - -"

have_dupes = False
for mac, vms in vms_macs.iteritems():
    if len(vms) > 1:
        have_dupes = True
        print "MAC: %s, VMs: %s" % (mac, vms)
        solve_conflict(mac, vms)

if not have_dupes:
    print "No MAC dupes found."
