<commit_before>#!/usr/bin/python

import base64
from binascii import hexlify
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from distutils.spawn import find_executable
from kvirt import common
from kvirt.common import error, pprint
from kvirt.defaults import UBUNTUS, METADATA_FIELDS
from pyVmomi import vim, vmodl
from pyVim import connect
import json
import os
import re
import requests
import random
from ssl import get_server_certificate
import sys
import tarfile
from tempfile import TemporaryDirectory
from threading import Thread
import time
import webbrowser
from zipfile import ZipFile
from kvirt.providers.vsphere.helpers import find, collectproperties, findvm, createfolder, changecd, convert, waitForMe
from kvirt.providers.vsphere.helpers import createscsispec, creatediskspec, createdvsnicspec, createclonespec
from kvirt.providers.vsphere.helpers import createnicspec, createisospec, deletedirectory, dssize, keep_lease_alive
from kvirt.providers.vsphere.helpers import create_filter_spec, get_all_obj, convert_properties, findvm2


class Ksphere:
    def __init__(self, host, user, password, datacenter, cluster, debug=False, isofolder=None,
                 filtervms=False, filteruser=False, filtertag=None, category='kcli', basefolder=None, dvs=True):
        si = connect.SmartConnectNoSSL(host=host, port=443, user=user, pwd=password)
        self.conn = si
        self.si = si
        self.vcip = host
        self.category = category
        self.url = f"https://{user}:{password}@{host}/sdk"
        self.user = user
        self.password = password
        self.rootFolder = si.content.rootFolder
        self.dc = find(si, self.rootFolder, vim.Datacenter, datacenter)
        self.macaddr = []
        self.clu = cluster
        self.isofolder = isofolder
        self.filtervms = filtervms
        self.filtervms = filtervms
        self.filteruser = filteruser
        self.filtertag = filtertag
        self.debug = debug
        self.networks = []
        self.dvs = dvs
        self.portgs = {}
        self.basefolder = basefolder
        return

    def set_networks(self):
        si = self.si
        view = si.content.viewManager.CreateContainerView(self.rootFolder, [vim.Network], True)
        netlist = collectproperties(si, view=view, objtype=vim.Network, pathset=['name'], includemors=True)
        self.networks = [o['obj'].name for o in netlist]
        if self.dvs:
            portgs = {}
            o = si.content.viewManager.CreateContainerView(self.rootFolder, [vim.DistributedVirtualSwitch], True)
            dvnetworks = o.view
            o.Destroy()
            for dvnetw in dvnetworks:
                uuid = dvnetw.uuid
                for portg in dvnetw.portgroup:
                    portgs[portg.name] = [uuid, portg.key]
            self.portgs = portgs

    def close(self):
        self.si.content.sessionManager.Logout()

    def exists(self, name):
        si = self.si
        dc = self.dc
        vmFolder = dc.vmFolder
        vm = findvm(si, vmFolder, name)
        return True if vm is not None else False

    def net_exists(self, name):
        print("not implemented")
        return

    def create(self, name, virttype=None, profile='kvirt', flavor=None, plan='kvirt', cpumodel='host-model',
               cpuflags=[], cpupinning=[], numcpus=2, memory=512, guestid='centos7_64Guest', pool='default', image=None,
               disks=[{'size': 10}], disksize=10, diskthin=True, diskinterface='virtio', nets=['default'], iso=None,
               vnc=False, cloudinit=True, reserveip=False, reservedns=False, reservehost=False, start=True, keys=None,
               cmds=[], ips=None, netmasks=None, gateway=None, nested=True, dns=None, domain=None, tunnel=False,
               files=[], enableroot=True, overrides={}, tags=[], storemetadata=False, sharedfolders=[],
               kernel=None, initrd=None, cmdline=None, placement=[], autostart=False, cpuhotplug=False,
               memoryhotplug=False, numamode=None, numa=[], pcidevices=[], tpm=False, rng=False, metadata={},
               securitygroups=[]):
        dc = self.dc
        vmFolder = dc.vmFolder
        diskmode = 'persistent'
        default_diskinterface = diskinterface
        default_diskthin = diskthin
        default_disksize = disksize
        default_pool = pool
        memory = int(memory)
        numcpus = int(numcpus)
        si = self.si
        basefolder = self.basefolder
        rootFolder = self.rootFolder
        if basefolder is not None:
            createfolder(si, dc.vmFolder, basefolder)
            basefolder = find(si, dc.vmFolder, vim.Folder, basefolder)
        else:
            basefolder = dc.vmFolder
        cluster = overrides.get('cluster')
        if cluster is not None:
            createfolder(si, basefolder, cluster)
            vmfolder = find(si, basefolder, vim.Folder, cluster)
        elif plan != 'kvirt':
            createfolder(si, basefolder, plan)
            vmfolder = find(si, basefolder, vim.Folder, plan)
        else:
            vmfolder = basefolder
        si = self.si
        clu = find(si, rootFolder, vim.ComputeResource, self.clu)
        resourcepool = clu.resourcePool
        if image is not None:
            rootFolder = self.rootFolder
            imageobj = findvm(si, rootFolder, image)
            if imageobj is None:
                return {'result': 'failure', 'reason': f"Image {image} not found"}
            clonespec = createclonespec(resourcepool)
            confspec = vim.vm.ConfigSpec()
            confspec.annotation = name
            confspec.memoryMB = memory
            confspec.numCPUs = numcpus
            extraconfig = []
            for entry in [field for field in metadata if field in METADATA_FIELDS]:
                opt = vim.option.OptionValue()
                opt.key = entry
                opt.value = metadata[entry]
                extraconfig.append(opt)
            if tags:
                opt = vim.option.OptionValue()
                opt.key = 'tags'
                opt.value = ','.join(tags)
                extraconfig.append(opt)
            clonespec.config = confspec
            clonespec.powerOn = False
            cloudinitiso = None
            if cloudinit:
                if image is not None and common.needs_ignition(image):
                    version = common.ignition_version(image)
                    ignitiondata = common.ignition(name=name, keys=keys, cmds=cmds, nets=nets, gateway=gateway, dns=dns,
                                                   domain=domain, files=files, enableroot=enableroot,
                                                   overrides=overrides, version=version, plan=plan, image=image)
                    ignitionopt = vim.option.OptionValue()
                    ignitionopt.key = 'guestinfo.ignition.config.data'
                    ignitionopt.value = base64.b64encode(ignitiondata.encode()).decode()
                    encodingopt = vim.option.OptionValue()
                    encodingopt.key = 'guestinfo.ignition.config.data.encoding'
                    encodingopt.value = 'base64'
                    extraconfig.extend([ignitionopt, encodingopt])
                else:
                    gcmds = []
                    if image is not None and 'cos' not in image and 'fedora-coreos' not in image:
                        lower = image.lower()
                        if lower.startswith('fedora') or lower.startswith('rhel') or lower.startswith('centos'):
                            gcmds.append('yum -y install open-vm-tools')
                        elif lower.startswith('debian') or [x for x in UBUNTUS if x in lower] or 'ubuntu' in lower:
                            gcmds.append('apt-get update')
                            gcmds.append('apt-get -f install open-vm-tools')
                        gcmds.append('systemctl enable --now vmtoolsd')
                    index = 0
                    if image is not None and image.startswith('rhel'):
                        subindex = [i for i, value in enumerate(cmds) if value.startswith('subscription-manager')]
                        if subindex:
                            index = subindex.pop() + 1
                    cmds = cmds[:index] + gcmds + cmds[index:]
                    # customspec = makecuspec(name, nets=nets, gateway=gateway, dns=dns, domain=domain)
                    # clonespec.customization = customspec
                    isofolder = self.isofolder if self.isofolder is not None else f"[{default_pool}]/{name}"
                    cloudinitiso = f"{isofolder}/{name}.ISO"
                    userdata, meta, netdata = common.cloudinit(name=name, keys=keys, cmds=cmds, nets=nets,
                                                               gateway=gateway, dns=dns, domain=domain,
                                                               files=files, enableroot=enableroot, overrides=overrides,
                                                               storemetadata=storemetadata, machine='vsphere',
                                                               image=image)
            for key in overrides:
                if key.startswith('guestinfo.'):
                    guestopt = vim.option.OptionValue()
                    guestopt.key = key
                    # guestopt.value = base64.b64encode(overrides[key].encode()).decode()
                    guestopt.value = overrides[key]
                    extraconfig.append(guestopt)
            confspec.extraConfig = extraconfig
            t = imageobj.CloneVM_Task(folder=vmfolder, name=name, spec=clonespec)
            waitForMe(t)
            if cloudinitiso is not None:
                with TemporaryDirectory() as tmpdir:
                    common.make_iso(name, tmpdir, userdata, meta, netdata)
                    cloudinitisofile = f"{tmpdir}/{name}.ISO"
                    if self.isofolder is not None:
                        isofolder = self.isofolder.split('/')
                        isopool = re.sub(r"[\[\]]", '', isofolder[0])
                        isofolder = isofolder[1]
                    else:
                        isopool = default_pool
                        isofolder = None
                    self._uploadimage(isopool, cloudinitisofile, name, isofolder=isofolder)
                vm = findvm(si, vmFolder, name)
                c = changecd(self.si, vm, cloudinitiso)
                waitForMe(c)
        datastores = {}
        confspec = vim.vm.ConfigSpec()
        confspec.name = name
        confspec.annotation = name
        confspec.memoryMB = memory
        confspec.numCPUs = numcpus
        confspec.extraConfig = []
        for entry in [field for field in metadata if field in METADATA_FIELDS]:
            opt = vim.option.OptionValue()
            opt.key = entry
            opt.value = metadata[entry]
            confspec.extraConfig.append(opt)
        confspec.guestId = 'centos7_64Guest'
        vmfi = vim.vm.FileInfo()
        filename = "[" + default_pool + "]"
        vmfi.vmPathName = filename
        confspec.files = vmfi
        if vnc:
            vncport = random.randint(5900, 7000)
            opt1 = vim.option.OptionValue()
            opt1.key = 'RemoteDisplay.vnc.port'
            opt1.value = vncport
            opt2 = vim.option.OptionValue()
            opt2.key = 'RemoteDisplay.vnc.enabled'
            opt2.value = "TRUE"
            confspec.extraConfig = [opt1, opt2]
        if image is None:
            t = vmfolder.CreateVM_Task(confspec, resourcepool)
            waitForMe(t)
        vm = find(si, dc.vmFolder, vim.VirtualMachine, name)
        currentdevices = vm.config.hardware.device
        currentdisks = [d for d in currentdevices if isinstance(d, vim.vm.device.VirtualDisk)]
        currentnics = [d for d in currentdevices if isinstance(d, vim.vm.device.VirtualEthernetCard)]
        confspec = vim.vm.ConfigSpec()
        devconfspec = []
        for index, disk in enumerate(disks):
            if disk is None:
                disksize = default_disksize
                diskthin = default_diskthin
                diskinterface = default_diskinterface
                diskpool = default_pool
            elif isinstance(disk, int):
                disksize = disk
                diskthin = default_diskthin
                diskinterface = default_diskinterface
                diskpool = default_pool
            elif isinstance(disk, str) and disk.isdigit():
                disksize = int(disk)
                diskthin = default_diskthin
                diskinterface = default_diskinterface
                diskpool = default_pool
            elif isinstance(disk, dict):
                disksize = disk.get('size', default_disksize)
                diskthin = disk.get('thin', default_diskthin)
                diskinterface = disk.get('interface', default_diskinterface)
                diskpool = disk.get('pool', default_pool)
            if index < len(currentdisks) and image is not None:
                currentdisk = currentdisks[index]
                currentsize = convert(1000 * currentdisk.capacityInKB, GB=False)
                if int(currentsize) < disksize:
                    pprint(f"Waiting for image disk {index} to be resized")
                    currentdisk.capacityInKB = disksize * 1048576
                    diskspec = vim.vm.ConfigSpec()
                    diskspec = vim.vm.device.VirtualDeviceSpec(device=currentdisk, operation="edit")
                    devconfspec.append(diskspec)
                continue
            disksize = disksize * 1048576
            if diskpool not in datastores:
                datastore = find(si, rootFolder, vim.Datastore, diskpool)
                if not datastore:
                    return {'result': 'failure', 'reason': f"Pool {diskpool} not found"}
                else:
                    datastores[diskpool] = datastore
            if index == 0:
                scsispec = createscsispec()
                devconfspec.append(scsispec)
            diskspec = creatediskspec(index, disksize, datastore, diskmode, diskthin, index)
            devconfspec.append(diskspec)
        # NICSPEC
        if not self.networks:
            self.set_networks()
        for index, net in enumerate(nets):
            netname = net['name'] if isinstance(net, dict) else net
            if netname == 'default':
                if image is not None:
                    continue
                else:
                    netname = 'VM Network'
            if index < len(currentnics):
                currentnic = currentnics[index]
                try:
                    currentnetwork = currentnic.backing.deviceName
                except:
                    currentswitchuuid = currentnic.backing.port.switchUuid
                    currentportgroupkey = currentnic.backing.port.portgroupKey
                    for dvsnet in self.portgs:
                        if self.portgs[dvsnet][0] == currentswitchuuid and\
                                self.portgs[dvsnet][1] == currentportgroupkey:
                            currentnetwork = dvsnet
                if currentnetwork != netname:
                    if netname in self.portgs:
                        switchuuid = self.portgs[netname][0]
                        portgroupkey = self.portgs[netname][1]
                        currentnic.backing.port.switchUuid = switchuuid
                        currentnic.backing.port.portgroupKey = portgroupkey
                        currentnic.backing.port.portKey = None
                        nicspec = vim.vm.device.VirtualDeviceSpec(device=currentnic, operation="edit")
                        devconfspec.append(nicspec)
                    elif netname in self.networks:
                        currentnic.backing.deviceName = netname
                        nicspec = vim.vm.device.VirtualDeviceSpec(device=currentnic, operation="edit")
                        devconfspec.append(nicspec)
                    else:
                        return {'result': 'failure', 'reason': f"Invalid network {netname}"}
                continue
            nicname = 'Network Adapter %d' % (index + 1)
            nictype = net['type'] if isinstance(net, dict) and 'type' in net else None
            if netname in self.portgs:
                switchuuid = self.portgs[netname][0]
                portgroupkey = self.portgs[netname][1]
                nicspec = createdvsnicspec(nicname, netname, switchuuid, portgroupkey, nictype=nictype)
            elif netname in self.networks:
                nicspec = createnicspec(nicname, netname, nictype=nictype)
            else:
                return {'result': 'failure', 'reason': f"Invalid network {netname}"}
            devconfspec.append(nicspec)
        if iso:
            if '/' not in iso:
                matchingisos = [i for i in self._getisos() if i.endswith(iso)]
                if matchingisos:
                    iso = matchingisos[0]
                else:
                    return {'result': 'failure', 'reason': f"Iso {iso} not found"}
            cdspec = createisospec(iso)
            devconfspec.append(cdspec)
            # bootoptions = vim.option.OptionValue(key='bios.bootDeviceClasses',value='allow:hd,cd,fd,net')
            # confspec.bootOptions = vim.vm.BootOptions(bootOrder=[vim.vm.BootOptions.BootableCdromDevice()])
        confspec.deviceChange = devconfspec
        if nested:
            confspec.nestedHVEnabled = True
        t = vm.Reconfigure(confspec)
        waitForMe(t)
        if 'vmgroup' in overrides:
            vmgroup = overrides['vmgroup']
            vmgroups = {}
            hostgroups = {}
            for group in clu.configurationEx.group:
                if hasattr(group, 'vm'):
                    vmgroups[group.name] = group
                else:
                    hostgroups[group.name] = group
            if vmgroup in vmgroups:
                vmgroup = vmgroups[vmgroup]
                vmgroup.vm.append(vm)
                vmgroupspec = vim.cluster.GroupSpec(info=vmgroup, operation='edit')
                groups_spec = vim.cluster.ConfigSpecEx(groupSpec=[vmgroupspec])
                t = clu.ReconfigureEx(groups_spec, modify=True)
                waitForMe(t)
            else:
                vmgroup = vim.cluster.VmGroup(name=vmgroup, vm=[vm])
                vmgroupspec = vim.cluster.GroupSpec(info=vmgroup, operation='add')
                groups_spec = vim.cluster.ConfigSpecEx(groupSpec=[vmgroupspec])
                t = clu.ReconfigureEx(groups_spec, modify=True)
                waitForMe(t)
            if 'hostgroup' in overrides and 'hostrule' in overrides:
                hostgroup = overrides['hostgroup']
                hostrule = overrides['hostrule']
                if hostgroup not in hostgroups:
                    msg = f"Hostgroup {hostgroup} not found. It needs to exist prior to vm's creation"
                    return {'result': 'failure', 'reason': msg}
                else:
                    hostgroup = hostgroups[hostgroup]
                    vmhostrulefound = False
                    for vmhostrule in clu.configurationEx.rule:
                        if vmhostrule.name == hostrule:
                            vmhostrulefound = True
                            break
                    if not vmhostrulefound:
                        pprint(f"Creating vmhost rule {hostrule}")
                        rule_obj = vim.cluster.VmHostRuleInfo(vmGroupName=hostrule, affineHostGroupName=hostgroup.name,
                                                              name=vmgroup.name, enabled=True, mandatory=True)
                        rulespec = vim.cluster.RuleSpec(info=rule_obj, operation='add')
                        groups_spec = vim.cluster.ConfigSpecEx(rulesSpec=[rulespec])
                        t = clu.ReconfigureEx(groups_spec, modify=True)
                        waitForMe(t)
        antipeers = overrides.get('antipeers', [])
        if antipeers and antipeers[-1] == name:
            antipeers_rule = '-'.join(antipeers)
            pprint(f"Creating anti affinity rule {antipeers_rule}")
            vms = []
            for member in antipeers:
                vm = findvm(si, vmFolder, member)
                if vm is None:
                    error(f"VM {member} not found")
                else:
                    vms.append(vm)
            if len(vms) > 1:
                rule_obj = vim.cluster.AntiAffinityRuleSpec(vm=vms, enabled=True, mandatory=True, name=antipeers_rule)
                rulespec = vim.cluster.RuleSpec(info=rule_obj, operation='add')
                groups_spec = vim.cluster.ConfigSpecEx(rulesSpec=[rulespec])
                t = clu.ReconfigureEx(groups_spec, modify=True)
                waitForMe(t)
        if start:
            t = vm.PowerOnVM_Task(None)
            waitForMe(t)
        if tags:
            pprint("Assigning tags")
            from kvirt.providers.vsphere.tagging import KsphereTag
            ktag = KsphereTag(self.vcip, self.user, self.password)
            categories = {}
            tags_ids = []
            for entry in tags:
                if isinstance(entry, str) and '=' in entry:
                    category, tag = entry.split('=')
                elif isinstance(entry, dict) and len(list(entry.keys())) == 1:
                    category, tag = list(entry.keys())[0], list(entry.values())[0]
                else:
                    category, tag = self.category, entry
                if category not in categories:
                    category_id = ktag.get_category_id(category)
                    if category_id is None:
                        category_id = ktag.create_category(category)
                    categories[category] = category_id
                category_id = categories[category]
                tag_id = ktag.get_tag_id(tag)
                if tag_id is None:
                    tag_id = ktag.create_tag(category_id, tag)
                tags_ids.append(tag_id)
            if tags_ids:
                vm_id = vm._moId
                ktag.add_tags(vm_id, tags_ids)
        return {'result': 'success'}

    def start(self, name):
        si = self.si
        dc = self.dc
        vmFolder = find(si, dc.vmFolder, vim.Folder, self.basefolder) if self.basefolder is not None else dc.vmFolder
        vm, info = findvm2(si, vmFolder, name)
        if vm is None:
            return {'result': 'failure', 'reason': f"VM {name} not found"}
        runtime = info['runtime']
        if runtime.powerState == "poweredOff":
            t = vm.PowerOnVM_Task(None)
            waitForMe(t)
        return {'result': 'success'}

    def stop(self, name, soft=False):
        si = self.si
        dc = self.dc
        vmFolder = find(si, dc.vmFolder, vim.Folder, self.basefolder) if self.basefolder is not None else dc.vmFolder
        vm, info = findvm2(si, vmFolder, name)
        if vm is None:
            return {'result': 'failure', 'reason': f"VM {name} not found"}
        runtime = info['runtime']
        if runtime.powerState == "poweredOn":
            t = vm.PowerOffVM_Task()
            waitForMe(t)
        return {'result': 'success'}

    def status(self, name):
        si = self.si
        dc = self.dc
        vmFolder = find(si, dc.vmFolder, vim.Folder, self.basefolder) if self.basefolder is not None else dc.vmFolder
        vm, info = findvm2(si, vmFolder, name)
        runtime = info['runtime']
        return runtime.powerState if vm is not None else ''

    def delete(self, name, snapshots=False):
        si = self.si
        dc = self.dc
        vmFolder = find(si, dc.vmFolder, vim.Folder, self.basefolder) if self.basefolder is not None else dc.vmFolder
        vm, info = findvm2(si, vmFolder, name)
        if vm is None:
            return {'result': 'failure', 'reason': f"VM {name} not found"}
        summary = info['summary']
        config = info['config']
        runtime = info['runtime']
        plan, image, kube = 'kvirt', None, None
        vmpath = summary.config.vmPathName.replace(f'/{name}.vmx', '')
        if config is not None:
            for entry in config.extraConfig:
                if entry.key == 'image':
                    image = entry.value
                if entry.key == 'plan':
                    plan = entry.value
                if entry.key == 'kube':
                    kube = entry.value
        if runtime.powerState == "poweredOn":
            t = vm.PowerOffVM_Task()
            waitForMe(t)
        t = vm.Destroy_Task()
        waitForMe(t)
        if image is not None and 'coreos' not in image and 'rhcos' not in image and\
                'fcos' not in image and vmpath.endswith(name):
            isopath = f"{self.isofolder}/{name}.ISO" if self.isofolder is not None else vmpath
            deletedirectory(si, dc, isopath)
        if kube is not None:
            clusterfolder = find(si, vmFolder, vim.Folder, kube)
            if clusterfolder is not None and len(clusterfolder.childEntity) == 0:
                clusterfolder.Destroy()
        elif plan != 'kvirt':
            planfolder = find(si, vmFolder, vim.Folder, plan)
            if planfolder is not None and len(planfolder.childEntity) == 0:
                try:
                    planfolder.Destroy()
                except Exception as e:
                    error(f"Couldn't delete plan folder {plan}. Hit {e}")
        return {'result': 'success'}

    def console(self, name, tunnel=False, web=False):
        si = self.si
        vcip = self.vcip
        dc = self.dc
        vmFolder = find(si, dc.vmFolder, vim.Folder, self.basefolder) if self.basefolder is not None else dc.vmFolder
        vm, info = findvm2(si, vmFolder, name)
        if vm is None:
            print(f"VM {name} not found")
            return
        runtime = info['runtime']
        config = info['config']
        if runtime.powerState == "poweredOff":
            print("VM down")
            return
        extraconfig = config.extraConfig
        vncfound = False
        for extra in extraconfig:
            key, value = extra.key, extra.value
            if 'vnc' in key and 'port' in key:
                vncfound = True
                vncport = value
                break
            else:
                continue
        if vncfound:
            host = runtime.host.name
            url = f"vnc://{host}:{vncport}"
            consolecommand = f"remote-viewer {url} &"
            if web:
                return url
            if self.debug or os.path.exists("/i_am_a_container"):
                print(consolecommand)
            if not os.path.exists("/i_am_a_container"):
                os.popen(consolecommand)
        else:
            content = si.RetrieveContent()
            sgid = content.about.instanceUuid
            cert = get_server_certificate((self.vcip, 443))
            cert_deserialize = x509.load_pem_x509_certificate(cert.encode(), default_backend())
            finger_print = hexlify(cert_deserialize.fingerprint(hashes.SHA1())).decode('utf-8')
            sha = ":".join([finger_print[i: i + 2] for i in range(0, len(finger_print), 2)])
            vcenter_data = content.setting
            vcenter_settings = vcenter_data.setting
            for item in vcenter_settings:
                key = getattr(item, 'key')
                if key == 'VirtualCenter.FQDN':
                    fqdn = getattr(item, 'value')
            sessionmanager = si.content.sessionManager
            session = sessionmanager.AcquireCloneTicket()
            vmid = vm._moId
            vmurl = f"https://{vcip}/ui/webconsole.html?"
            vmurl += f"vmId={vmid}&vmName={name}&serverGuid={sgid}&host={fqdn}&sessionTicket={session}&thumbprint={sha}"
            if web:
                return vmurl
            if self.debug or os.path.exists("/i_am_a_container"):
                msg = f"Open the following url:\n{vmurl}" if os.path.exists("/i_am_a_container") else vmurl
                pprint(msg)
            else:
                pprint(f"Opening url {vmurl}")
                webbrowser.open(vmurl, new=2, autoraise=True)

    def info(self, name, output='plain', fields=[], values=False, vm=None, debug=False):
        translation = {'poweredOff': 'down', 'poweredOn': 'up', 'suspended': 'suspended'}
        image = None
        yamlinfo = {}
        si = self.si
        if vm is None:
            listinfo = False
            dc = self.dc
            basefolder = self.basefolder
            vmFolder = find(si, dc.vmFolder, vim.Folder, basefolder) if basefolder is not None else dc.vmFolder
            obj, vm = findvm2(si, vmFolder, name)
            if vm is None:
                error(f"VM {name} not found")
                return {}
        else:
            listinfo = True
        summary = vm['summary']
        config = vm['config']
        runtime = vm['runtime']
        guest = vm['guest']
        yamlinfo['name'] = name
        yamlinfo['id'] = summary.config.instanceUuid
        yamlinfo['cpus'] = config.hardware.numCPU
        yamlinfo['memory'] = config.hardware.memoryMB
        yamlinfo['status'] = translation[runtime.powerState]
        yamlinfo['nets'] = []
        yamlinfo['disks'] = []
        if runtime.powerState == "poweredOn":
            yamlinfo['host'] = runtime.host.name
            for nic in guest.net:
                if 'ip' not in yamlinfo and nic.ipAddress:
                    yamlinfo['ip'] = nic.ipAddress[0]
        for entry in config.extraConfig:
            if entry.key in METADATA_FIELDS:
                yamlinfo[entry.key] = entry.value
            if entry.key == 'image':
                image = entry.value
                yamlinfo['user'] = common.get_user(entry.value)
            if entry.key == 'tags':
                yamlinfo['tags'] = entry.value
        if listinfo:
            return yamlinfo
        if image is None and 'kubetype' in yamlinfo and yamlinfo['kubetype'] == 'openshift':
            yamlinfo['user'] = 'core'
        if debug:
            yamlinfo['debug'] = config
        if not self.networks:
            self.set_networks()
        devices = config.hardware.device
        for number, dev in enumerate(devices):
            if "addressType" in dir(dev):
                try:
                    network = dev.backing.deviceName
                except:
                    switchuuid = dev.backing.port.switchUuid
                    portgroupkey = dev.backing.port.portgroupKey
                    for dvsnet in self.portgs:
                        if self.portgs[dvsnet][0] == switchuuid and self.portgs[dvsnet][1] == portgroupkey:
                            network = dvsnet
                device = dev.deviceInfo.label
                devicename = type(dev).__name__.replace('vim.vm.device.Virtual', '').lower()
                networktype = devicename
                mac = dev.macAddress
                net = {'device': device, 'mac': mac, 'net': network, 'type': networktype}
                yamlinfo['nets'].append(net)
            if type(dev).__name__ == 'vim.vm.device.VirtualDisk':
                device = f"disk{dev.unitNumber}"
                disksize = convert(1000 * dev.capacityInKB, GB=False)
                diskformat = dev.backing.diskMode
                drivertype = 'thin' if dev.backing.thinProvisioned else 'thick'
                path = dev.backing.datastore.name
                disk = {'device': device, 'size': int(disksize), 'format': diskformat, 'type': drivertype,
                        'path': path}
                yamlinfo['disks'].append(disk)
        return yamlinfo

    def list(self):
        si = self.si
        dc = self.dc
        content = si.content
        vms = []
        folder = find(si, dc.vmFolder, vim.Folder, self.basefolder) if self.basefolder is not None else dc.vmFolder
        all_vms = get_all_obj(content, [vim.VirtualMachine], folder=folder)
        prop_collector = content.propertyCollector
        props = ['runtime', 'config', 'summary', 'guest']
        filter_spec = create_filter_spec(all_vms, props)<commit_msg>vsphere: dont process vm list when empty<commit_after>#!/usr/bin/python

import base64
from binascii import hexlify
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from distutils.spawn import find_executable
from kvirt import common
from kvirt.common import error, pprint
from kvirt.defaults import UBUNTUS, METADATA_FIELDS
from pyVmomi import vim, vmodl
from pyVim import connect
import json
import os
import re
import requests
import random
from ssl import get_server_certificate
import sys
import tarfile
from tempfile import TemporaryDirectory
from threading import Thread
import time
import webbrowser
from zipfile import ZipFile
from kvirt.providers.vsphere.helpers import find, collectproperties, findvm, createfolder, changecd, convert, waitForMe
from kvirt.providers.vsphere.helpers import createscsispec, creatediskspec, createdvsnicspec, createclonespec
from kvirt.providers.vsphere.helpers import createnicspec, createisospec, deletedirectory, dssize, keep_lease_alive
from kvirt.providers.vsphere.helpers import create_filter_spec, get_all_obj, convert_properties, findvm2


class Ksphere:
    def __init__(self, host, user, password, datacenter, cluster, debug=False, isofolder=None,
                 filtervms=False, filteruser=False, filtertag=None, category='kcli', basefolder=None, dvs=True):
        si = connect.SmartConnectNoSSL(host=host, port=443, user=user, pwd=password)
        self.conn = si
        self.si = si
        self.vcip = host
        self.category = category
        self.url = f"https://{user}:{password}@{host}/sdk"
        self.user = user
        self.password = password
        self.rootFolder = si.content.rootFolder
        self.dc = find(si, self.rootFolder, vim.Datacenter, datacenter)
        self.macaddr = []
        self.clu = cluster
        self.isofolder = isofolder
        self.filtervms = filtervms
        self.filtervms = filtervms
        self.filteruser = filteruser
        self.filtertag = filtertag
        self.debug = debug
        self.networks = []
        self.dvs = dvs
        self.portgs = {}
        self.basefolder = basefolder
        return

    def set_networks(self):
        si = self.si
        view = si.content.viewManager.CreateContainerView(self.rootFolder, [vim.Network], True)
        netlist = collectproperties(si, view=view, objtype=vim.Network, pathset=['name'], includemors=True)
        self.networks = [o['obj'].name for o in netlist]
        if self.dvs:
            portgs = {}
            o = si.content.viewManager.CreateContainerView(self.rootFolder, [vim.DistributedVirtualSwitch], True)
            dvnetworks = o.view
            o.Destroy()
            for dvnetw in dvnetworks:
                uuid = dvnetw.uuid
                for portg in dvnetw.portgroup:
                    portgs[portg.name] = [uuid, portg.key]
            self.portgs = portgs

    def close(self):
        self.si.content.sessionManager.Logout()

    def exists(self, name):
        si = self.si
        dc = self.dc
        vmFolder = dc.vmFolder
        vm = findvm(si, vmFolder, name)
        return True if vm is not None else False

    def net_exists(self, name):
        print("not implemented")
        return

    def create(self, name, virttype=None, profile='kvirt', flavor=None, plan='kvirt', cpumodel='host-model',
               cpuflags=[], cpupinning=[], numcpus=2, memory=512, guestid='centos7_64Guest', pool='default', image=None,
               disks=[{'size': 10}], disksize=10, diskthin=True, diskinterface='virtio', nets=['default'], iso=None,
               vnc=False, cloudinit=True, reserveip=False, reservedns=False, reservehost=False, start=True, keys=None,
               cmds=[], ips=None, netmasks=None, gateway=None, nested=True, dns=None, domain=None, tunnel=False,
               files=[], enableroot=True, overrides={}, tags=[], storemetadata=False, sharedfolders=[],
               kernel=None, initrd=None, cmdline=None, placement=[], autostart=False, cpuhotplug=False,
               memoryhotplug=False, numamode=None, numa=[], pcidevices=[], tpm=False, rng=False, metadata={},
               securitygroups=[]):
        dc = self.dc
        vmFolder = dc.vmFolder
        diskmode = 'persistent'
        default_diskinterface = diskinterface
        default_diskthin = diskthin
        default_disksize = disksize
        default_pool = pool
        memory = int(memory)
        numcpus = int(numcpus)
        si = self.si
        basefolder = self.basefolder
        rootFolder = self.rootFolder
        if basefolder is not None:
            createfolder(si, dc.vmFolder, basefolder)
            basefolder = find(si, dc.vmFolder, vim.Folder, basefolder)
        else:
            basefolder = dc.vmFolder
        cluster = overrides.get('cluster')
        if cluster is not None:
            createfolder(si, basefolder, cluster)
            vmfolder = find(si, basefolder, vim.Folder, cluster)
        elif plan != 'kvirt':
            createfolder(si, basefolder, plan)
            vmfolder = find(si, basefolder, vim.Folder, plan)
        else:
            vmfolder = basefolder
        si = self.si
        clu = find(si, rootFolder, vim.ComputeResource, self.clu)
        resourcepool = clu.resourcePool
        if image is not None:
            rootFolder = self.rootFolder
            imageobj = findvm(si, rootFolder, image)
            if imageobj is None:
                return {'result': 'failure', 'reason': f"Image {image} not found"}
            clonespec = createclonespec(resourcepool)
            confspec = vim.vm.ConfigSpec()
            confspec.annotation = name
            confspec.memoryMB = memory
            confspec.numCPUs = numcpus
            extraconfig = []
            for entry in [field for field in metadata if field in METADATA_FIELDS]:
                opt = vim.option.OptionValue()
                opt.key = entry
                opt.value = metadata[entry]
                extraconfig.append(opt)
            if tags:
                opt = vim.option.OptionValue()
                opt.key = 'tags'
                opt.value = ','.join(tags)
                extraconfig.append(opt)
            clonespec.config = confspec
            clonespec.powerOn = False
            cloudinitiso = None
            if cloudinit:
                if image is not None and common.needs_ignition(image):
                    version = common.ignition_version(image)
                    ignitiondata = common.ignition(name=name, keys=keys, cmds=cmds, nets=nets, gateway=gateway, dns=dns,
                                                   domain=domain, files=files, enableroot=enableroot,
                                                   overrides=overrides, version=version, plan=plan, image=image)
                    ignitionopt = vim.option.OptionValue()
                    ignitionopt.key = 'guestinfo.ignition.config.data'
                    ignitionopt.value = base64.b64encode(ignitiondata.encode()).decode()
                    encodingopt = vim.option.OptionValue()
                    encodingopt.key = 'guestinfo.ignition.config.data.encoding'
                    encodingopt.value = 'base64'
                    extraconfig.extend([ignitionopt, encodingopt])
                else:
                    gcmds = []
                    if image is not None and 'cos' not in image and 'fedora-coreos' not in image:
                        lower = image.lower()
                        if lower.startswith('fedora') or lower.startswith('rhel') or lower.startswith('centos'):
                            gcmds.append('yum -y install open-vm-tools')
                        elif lower.startswith('debian') or [x for x in UBUNTUS if x in lower] or 'ubuntu' in lower:
                            gcmds.append('apt-get update')
                            gcmds.append('apt-get -f install open-vm-tools')
                        gcmds.append('systemctl enable --now vmtoolsd')
                    index = 0
                    if image is not None and image.startswith('rhel'):
                        subindex = [i for i, value in enumerate(cmds) if value.startswith('subscription-manager')]
                        if subindex:
                            index = subindex.pop() + 1
                    cmds = cmds[:index] + gcmds + cmds[index:]
                    # customspec = makecuspec(name, nets=nets, gateway=gateway, dns=dns, domain=domain)
                    # clonespec.customization = customspec
                    isofolder = self.isofolder if self.isofolder is not None else f"[{default_pool}]/{name}"
                    cloudinitiso = f"{isofolder}/{name}.ISO"
                    userdata, meta, netdata = common.cloudinit(name=name, keys=keys, cmds=cmds, nets=nets,
                                                               gateway=gateway, dns=dns, domain=domain,
                                                               files=files, enableroot=enableroot, overrides=overrides,
                                                               storemetadata=storemetadata, machine='vsphere',
                                                               image=image)
            for key in overrides:
                if key.startswith('guestinfo.'):
                    guestopt = vim.option.OptionValue()
                    guestopt.key = key
                    # guestopt.value = base64.b64encode(overrides[key].encode()).decode()
                    guestopt.value = overrides[key]
                    extraconfig.append(guestopt)
            confspec.extraConfig = extraconfig
            t = imageobj.CloneVM_Task(folder=vmfolder, name=name, spec=clonespec)
            waitForMe(t)
            if cloudinitiso is not None:
                with TemporaryDirectory() as tmpdir:
                    common.make_iso(name, tmpdir, userdata, meta, netdata)
                    cloudinitisofile = f"{tmpdir}/{name}.ISO"
                    if self.isofolder is not None:
                        isofolder = self.isofolder.split('/')
                        isopool = re.sub(r"[\[\]]", '', isofolder[0])
                        isofolder = isofolder[1]
                    else:
                        isopool = default_pool
                        isofolder = None
                    self._uploadimage(isopool, cloudinitisofile, name, isofolder=isofolder)
                vm = findvm(si, vmFolder, name)
                c = changecd(self.si, vm, cloudinitiso)
                waitForMe(c)
        datastores = {}
        confspec = vim.vm.ConfigSpec()
        confspec.name = name
        confspec.annotation = name
        confspec.memoryMB = memory
        confspec.numCPUs = numcpus
        confspec.extraConfig = []
        for entry in [field for field in metadata if field in METADATA_FIELDS]:
            opt = vim.option.OptionValue()
            opt.key = entry
            opt.value = metadata[entry]
            confspec.extraConfig.append(opt)
        confspec.guestId = 'centos7_64Guest'
        vmfi = vim.vm.FileInfo()
        filename = "[" + default_pool + "]"
        vmfi.vmPathName = filename
        confspec.files = vmfi
        if vnc:
            vncport = random.randint(5900, 7000)
            opt1 = vim.option.OptionValue()
            opt1.key = 'RemoteDisplay.vnc.port'
            opt1.value = vncport
            opt2 = vim.option.OptionValue()
            opt2.key = 'RemoteDisplay.vnc.enabled'
            opt2.value = "TRUE"
            confspec.extraConfig = [opt1, opt2]
        if image is None:
            t = vmfolder.CreateVM_Task(confspec, resourcepool)
            waitForMe(t)
        vm = find(si, dc.vmFolder, vim.VirtualMachine, name)
        currentdevices = vm.config.hardware.device
        currentdisks = [d for d in currentdevices if isinstance(d, vim.vm.device.VirtualDisk)]
        currentnics = [d for d in currentdevices if isinstance(d, vim.vm.device.VirtualEthernetCard)]
        confspec = vim.vm.ConfigSpec()
        devconfspec = []
        for index, disk in enumerate(disks):
            if disk is None:
                disksize = default_disksize
                diskthin = default_diskthin
                diskinterface = default_diskinterface
                diskpool = default_pool
            elif isinstance(disk, int):
                disksize = disk
                diskthin = default_diskthin
                diskinterface = default_diskinterface
                diskpool = default_pool
            elif isinstance(disk, str) and disk.isdigit():
                disksize = int(disk)
                diskthin = default_diskthin
                diskinterface = default_diskinterface
                diskpool = default_pool
            elif isinstance(disk, dict):
                disksize = disk.get('size', default_disksize)
                diskthin = disk.get('thin', default_diskthin)
                diskinterface = disk.get('interface', default_diskinterface)
                diskpool = disk.get('pool', default_pool)
            if index < len(currentdisks) and image is not None:
                currentdisk = currentdisks[index]
                currentsize = convert(1000 * currentdisk.capacityInKB, GB=False)
                if int(currentsize) < disksize:
                    pprint(f"Waiting for image disk {index} to be resized")
                    currentdisk.capacityInKB = disksize * 1048576
                    diskspec = vim.vm.ConfigSpec()
                    diskspec = vim.vm.device.VirtualDeviceSpec(device=currentdisk, operation="edit")
                    devconfspec.append(diskspec)
                continue
            disksize = disksize * 1048576
            if diskpool not in datastores:
                datastore = find(si, rootFolder, vim.Datastore, diskpool)
                if not datastore:
                    return {'result': 'failure', 'reason': f"Pool {diskpool} not found"}
                else:
                    datastores[diskpool] = datastore
            if index == 0:
                scsispec = createscsispec()
                devconfspec.append(scsispec)
            diskspec = creatediskspec(index, disksize, datastore, diskmode, diskthin, index)
            devconfspec.append(diskspec)
        # NICSPEC
        if not self.networks:
            self.set_networks()
        for index, net in enumerate(nets):
            netname = net['name'] if isinstance(net, dict) else net
            if netname == 'default':
                if image is not None:
                    continue
                else:
                    netname = 'VM Network'
            if index < len(currentnics):
                currentnic = currentnics[index]
                try:
                    currentnetwork = currentnic.backing.deviceName
                except:
                    currentswitchuuid = currentnic.backing.port.switchUuid
                    currentportgroupkey = currentnic.backing.port.portgroupKey
                    for dvsnet in self.portgs:
                        if self.portgs[dvsnet][0] == currentswitchuuid and\
                                self.portgs[dvsnet][1] == currentportgroupkey:
                            currentnetwork = dvsnet
                if currentnetwork != netname:
                    if netname in self.portgs:
                        switchuuid = self.portgs[netname][0]
                        portgroupkey = self.portgs[netname][1]
                        currentnic.backing.port.switchUuid = switchuuid
                        currentnic.backing.port.portgroupKey = portgroupkey
                        currentnic.backing.port.portKey = None
                        nicspec = vim.vm.device.VirtualDeviceSpec(device=currentnic, operation="edit")
                        devconfspec.append(nicspec)
                    elif netname in self.networks:
                        currentnic.backing.deviceName = netname
                        nicspec = vim.vm.device.VirtualDeviceSpec(device=currentnic, operation="edit")
                        devconfspec.append(nicspec)
                    else:
                        return {'result': 'failure', 'reason': f"Invalid network {netname}"}
                continue
            nicname = 'Network Adapter %d' % (index + 1)
            nictype = net['type'] if isinstance(net, dict) and 'type' in net else None
            if netname in self.portgs:
                switchuuid = self.portgs[netname][0]
                portgroupkey = self.portgs[netname][1]
                nicspec = createdvsnicspec(nicname, netname, switchuuid, portgroupkey, nictype=nictype)
            elif netname in self.networks:
                nicspec = createnicspec(nicname, netname, nictype=nictype)
            else:
                return {'result': 'failure', 'reason': f"Invalid network {netname}"}
            devconfspec.append(nicspec)
        if iso:
            if '/' not in iso:
                matchingisos = [i for i in self._getisos() if i.endswith(iso)]
                if matchingisos:
                    iso = matchingisos[0]
                else:
                    return {'result': 'failure', 'reason': f"Iso {iso} not found"}
            cdspec = createisospec(iso)
            devconfspec.append(cdspec)
            # bootoptions = vim.option.OptionValue(key='bios.bootDeviceClasses',value='allow:hd,cd,fd,net')
            # confspec.bootOptions = vim.vm.BootOptions(bootOrder=[vim.vm.BootOptions.BootableCdromDevice()])
        confspec.deviceChange = devconfspec
        if nested:
            confspec.nestedHVEnabled = True
        t = vm.Reconfigure(confspec)
        waitForMe(t)
        if 'vmgroup' in overrides:
            vmgroup = overrides['vmgroup']
            vmgroups = {}
            hostgroups = {}
            for group in clu.configurationEx.group:
                if hasattr(group, 'vm'):
                    vmgroups[group.name] = group
                else:
                    hostgroups[group.name] = group
            if vmgroup in vmgroups:
                vmgroup = vmgroups[vmgroup]
                vmgroup.vm.append(vm)
                vmgroupspec = vim.cluster.GroupSpec(info=vmgroup, operation='edit')
                groups_spec = vim.cluster.ConfigSpecEx(groupSpec=[vmgroupspec])
                t = clu.ReconfigureEx(groups_spec, modify=True)
                waitForMe(t)
            else:
                vmgroup = vim.cluster.VmGroup(name=vmgroup, vm=[vm])
                vmgroupspec = vim.cluster.GroupSpec(info=vmgroup, operation='add')
                groups_spec = vim.cluster.ConfigSpecEx(groupSpec=[vmgroupspec])
                t = clu.ReconfigureEx(groups_spec, modify=True)
                waitForMe(t)
            if 'hostgroup' in overrides and 'hostrule' in overrides:
                hostgroup = overrides['hostgroup']
                hostrule = overrides['hostrule']
                if hostgroup not in hostgroups:
                    msg = f"Hostgroup {hostgroup} not found. It needs to exist prior to vm's creation"
                    return {'result': 'failure', 'reason': msg}
                else:
                    hostgroup = hostgroups[hostgroup]
                    vmhostrulefound = False
                    for vmhostrule in clu.configurationEx.rule:
                        if vmhostrule.name == hostrule:
                            vmhostrulefound = True
                            break
                    if not vmhostrulefound:
                        pprint(f"Creating vmhost rule {hostrule}")
                        rule_obj = vim.cluster.VmHostRuleInfo(vmGroupName=hostrule, affineHostGroupName=hostgroup.name,
                                                              name=vmgroup.name, enabled=True, mandatory=True)
                        rulespec = vim.cluster.RuleSpec(info=rule_obj, operation='add')
                        groups_spec = vim.cluster.ConfigSpecEx(rulesSpec=[rulespec])
                        t = clu.ReconfigureEx(groups_spec, modify=True)
                        waitForMe(t)
        antipeers = overrides.get('antipeers', [])
        if antipeers and antipeers[-1] == name:
            antipeers_rule = '-'.join(antipeers)
            pprint(f"Creating anti affinity rule {antipeers_rule}")
            vms = []
            for member in antipeers:
                vm = findvm(si, vmFolder, member)
                if vm is None:
                    error(f"VM {member} not found")
                else:
                    vms.append(vm)
            if len(vms) > 1:
                rule_obj = vim.cluster.AntiAffinityRuleSpec(vm=vms, enabled=True, mandatory=True, name=antipeers_rule)
                rulespec = vim.cluster.RuleSpec(info=rule_obj, operation='add')
                groups_spec = vim.cluster.ConfigSpecEx(rulesSpec=[rulespec])
                t = clu.ReconfigureEx(groups_spec, modify=True)
                waitForMe(t)
        if start:
            t = vm.PowerOnVM_Task(None)
            waitForMe(t)
        if tags:
            pprint("Assigning tags")
            from kvirt.providers.vsphere.tagging import KsphereTag
            ktag = KsphereTag(self.vcip, self.user, self.password)
            categories = {}
            tags_ids = []
            for entry in tags:
                if isinstance(entry, str) and '=' in entry:
                    category, tag = entry.split('=')
                elif isinstance(entry, dict) and len(list(entry.keys())) == 1:
                    category, tag = list(entry.keys())[0], list(entry.values())[0]
                else:
                    category, tag = self.category, entry
                if category not in categories:
                    category_id = ktag.get_category_id(category)
                    if category_id is None:
                        category_id = ktag.create_category(category)
                    categories[category] = category_id
                category_id = categories[category]
                tag_id = ktag.get_tag_id(tag)
                if tag_id is None:
                    tag_id = ktag.create_tag(category_id, tag)
                tags_ids.append(tag_id)
            if tags_ids:
                vm_id = vm._moId
                ktag.add_tags(vm_id, tags_ids)
        return {'result': 'success'}

    def start(self, name):
        si = self.si
        dc = self.dc
        vmFolder = find(si, dc.vmFolder, vim.Folder, self.basefolder) if self.basefolder is not None else dc.vmFolder
        vm, info = findvm2(si, vmFolder, name)
        if vm is None:
            return {'result': 'failure', 'reason': f"VM {name} not found"}
        runtime = info['runtime']
        if runtime.powerState == "poweredOff":
            t = vm.PowerOnVM_Task(None)
            waitForMe(t)
        return {'result': 'success'}

    def stop(self, name, soft=False):
        si = self.si
        dc = self.dc
        vmFolder = find(si, dc.vmFolder, vim.Folder, self.basefolder) if self.basefolder is not None else dc.vmFolder
        vm, info = findvm2(si, vmFolder, name)
        if vm is None:
            return {'result': 'failure', 'reason': f"VM {name} not found"}
        runtime = info['runtime']
        if runtime.powerState == "poweredOn":
            t = vm.PowerOffVM_Task()
            waitForMe(t)
        return {'result': 'success'}

    def status(self, name):
        si = self.si
        dc = self.dc
        vmFolder = find(si, dc.vmFolder, vim.Folder, self.basefolder) if self.basefolder is not None else dc.vmFolder
        vm, info = findvm2(si, vmFolder, name)
        runtime = info['runtime']
        return runtime.powerState if vm is not None else ''

    def delete(self, name, snapshots=False):
        si = self.si
        dc = self.dc
        vmFolder = find(si, dc.vmFolder, vim.Folder, self.basefolder) if self.basefolder is not None else dc.vmFolder
        vm, info = findvm2(si, vmFolder, name)
        if vm is None:
            return {'result': 'failure', 'reason': f"VM {name} not found"}
        summary = info['summary']
        config = info['config']
        runtime = info['runtime']
        plan, image, kube = 'kvirt', None, None
        vmpath = summary.config.vmPathName.replace(f'/{name}.vmx', '')
        if config is not None:
            for entry in config.extraConfig:
                if entry.key == 'image':
                    image = entry.value
                if entry.key == 'plan':
                    plan = entry.value
                if entry.key == 'kube':
                    kube = entry.value
        if runtime.powerState == "poweredOn":
            t = vm.PowerOffVM_Task()
            waitForMe(t)
        t = vm.Destroy_Task()
        waitForMe(t)
        if image is not None and 'coreos' not in image and 'rhcos' not in image and\
                'fcos' not in image and vmpath.endswith(name):
            isopath = f"{self.isofolder}/{name}.ISO" if self.isofolder is not None else vmpath
            deletedirectory(si, dc, isopath)
        if kube is not None:
            clusterfolder = find(si, vmFolder, vim.Folder, kube)
            if clusterfolder is not None and len(clusterfolder.childEntity) == 0:
                clusterfolder.Destroy()
        elif plan != 'kvirt':
            planfolder = find(si, vmFolder, vim.Folder, plan)
            if planfolder is not None and len(planfolder.childEntity) == 0:
                try:
                    planfolder.Destroy()
                except Exception as e:
                    error(f"Couldn't delete plan folder {plan}. Hit {e}")
        return {'result': 'success'}

    def console(self, name, tunnel=False, web=False):
        si = self.si
        vcip = self.vcip
        dc = self.dc
        vmFolder = find(si, dc.vmFolder, vim.Folder, self.basefolder) if self.basefolder is not None else dc.vmFolder
        vm, info = findvm2(si, vmFolder, name)
        if vm is None:
            print(f"VM {name} not found")
            return
        runtime = info['runtime']
        config = info['config']
        if runtime.powerState == "poweredOff":
            print("VM down")
            return
        extraconfig = config.extraConfig
        vncfound = False
        for extra in extraconfig:
            key, value = extra.key, extra.value
            if 'vnc' in key and 'port' in key:
                vncfound = True
                vncport = value
                break
            else:
                continue
        if vncfound:
            host = runtime.host.name
            url = f"vnc://{host}:{vncport}"
            consolecommand = f"remote-viewer {url} &"
            if web:
                return url
            if self.debug or os.path.exists("/i_am_a_container"):
                print(consolecommand)
            if not os.path.exists("/i_am_a_container"):
                os.popen(consolecommand)
        else:
            content = si.RetrieveContent()
            sgid = content.about.instanceUuid
            cert = get_server_certificate((self.vcip, 443))
            cert_deserialize = x509.load_pem_x509_certificate(cert.encode(), default_backend())
            finger_print = hexlify(cert_deserialize.fingerprint(hashes.SHA1())).decode('utf-8')
            sha = ":".join([finger_print[i: i + 2] for i in range(0, len(finger_print), 2)])
            vcenter_data = content.setting
            vcenter_settings = vcenter_data.setting
            for item in vcenter_settings:
                key = getattr(item, 'key')
                if key == 'VirtualCenter.FQDN':
                    fqdn = getattr(item, 'value')
            sessionmanager = si.content.sessionManager
            session = sessionmanager.AcquireCloneTicket()
            vmid = vm._moId
            vmurl = f"https://{vcip}/ui/webconsole.html?"
            vmurl += f"vmId={vmid}&vmName={name}&serverGuid={sgid}&host={fqdn}&sessionTicket={session}&thumbprint={sha}"
            if web:
                return vmurl
            if self.debug or os.path.exists("/i_am_a_container"):
                msg = f"Open the following url:\n{vmurl}" if os.path.exists("/i_am_a_container") else vmurl
                pprint(msg)
            else:
                pprint(f"Opening url {vmurl}")
                webbrowser.open(vmurl, new=2, autoraise=True)

    def info(self, name, output='plain', fields=[], values=False, vm=None, debug=False):
        translation = {'poweredOff': 'down', 'poweredOn': 'up', 'suspended': 'suspended'}
        image = None
        yamlinfo = {}
        si = self.si
        if vm is None:
            listinfo = False
            dc = self.dc
            basefolder = self.basefolder
            vmFolder = find(si, dc.vmFolder, vim.Folder, basefolder) if basefolder is not None else dc.vmFolder
            obj, vm = findvm2(si, vmFolder, name)
            if vm is None:
                error(f"VM {name} not found")
                return {}
        else:
            listinfo = True
        summary = vm['summary']
        config = vm['config']
        runtime = vm['runtime']
        guest = vm['guest']
        yamlinfo['name'] = name
        yamlinfo['id'] = summary.config.instanceUuid
        yamlinfo['cpus'] = config.hardware.numCPU
        yamlinfo['memory'] = config.hardware.memoryMB
        yamlinfo['status'] = translation[runtime.powerState]
        yamlinfo['nets'] = []
        yamlinfo['disks'] = []
        if runtime.powerState == "poweredOn":
            yamlinfo['host'] = runtime.host.name
            for nic in guest.net:
                if 'ip' not in yamlinfo and nic.ipAddress:
                    yamlinfo['ip'] = nic.ipAddress[0]
        for entry in config.extraConfig:
            if entry.key in METADATA_FIELDS:
                yamlinfo[entry.key] = entry.value
            if entry.key == 'image':
                image = entry.value
                yamlinfo['user'] = common.get_user(entry.value)
            if entry.key == 'tags':
                yamlinfo['tags'] = entry.value
        if listinfo:
            return yamlinfo
        if image is None and 'kubetype' in yamlinfo and yamlinfo['kubetype'] == 'openshift':
            yamlinfo['user'] = 'core'
        if debug:
            yamlinfo['debug'] = config
        if not self.networks:
            self.set_networks()
        devices = config.hardware.device
        for number, dev in enumerate(devices):
            if "addressType" in dir(dev):
                try:
                    network = dev.backing.deviceName
                except:
                    switchuuid = dev.backing.port.switchUuid
                    portgroupkey = dev.backing.port.portgroupKey
                    for dvsnet in self.portgs:
                        if self.portgs[dvsnet][0] == switchuuid and self.portgs[dvsnet][1] == portgroupkey:
                            network = dvsnet
                device = dev.deviceInfo.label
                devicename = type(dev).__name__.replace('vim.vm.device.Virtual', '').lower()
                networktype = devicename
                mac = dev.macAddress
                net = {'device': device, 'mac': mac, 'net': network, 'type': networktype}
                yamlinfo['nets'].append(net)
            if type(dev).__name__ == 'vim.vm.device.VirtualDisk':
                device = f"disk{dev.unitNumber}"
                disksize = convert(1000 * dev.capacityInKB, GB=False)
                diskformat = dev.backing.diskMode
                drivertype = 'thin' if dev.backing.thinProvisioned else 'thick'
                path = dev.backing.datastore.name
                disk = {'device': device, 'size': int(disksize), 'format': diskformat, 'type': drivertype,
                        'path': path}
                yamlinfo['disks'].append(disk)
        return yamlinfo

    def list(self):
        si = self.si
        dc = self.dc
        content = si.content
        vms = []
        folder = find(si, dc.vmFolder, vim.Folder, self.basefolder) if self.basefolder is not None else dc.vmFolder
        all_vms = get_all_obj(content, [vim.VirtualMachine], folder=folder)
        if not all_vms:
            return vms
        prop_collector = content.propertyCollector
        props = ['runtime', 'config', 'summary', 'guest']
        filter_spec = create_filter_spec(all_vms, props)
{'fraction_non_alphanumeric': 0.09622600551989333, 'fraction_numerical': 0.003566223214562595, 'mean_word_length': 1.7245268671848597, 'pattern_counts': {'":': 12, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 28, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3611162', 'n_tokens_mistral': 18207, 'n_tokens_neox': 17449, 'n_words': 4170}