mirror of
https://github.com/retspen/webvirtcloud
synced 2026-03-23 11:04:49 +00:00
Added V2 from scratch
This commit is contained in:
parent
5c2232f4e8
commit
6c2925a35d
478 changed files with 21437 additions and 134206 deletions
2
backend/libvmgr/__init__.py
Normal file
2
backend/libvmgr/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
__author__ = "Anatoliy Guskov"
|
||||
__license__ = "Apache 2.0"
|
||||
499
backend/libvmgr/connect.py
Normal file
499
backend/libvmgr/connect.py
Normal file
|
|
@ -0,0 +1,499 @@
|
|||
import libvirt
|
||||
import threading
|
||||
import socket
|
||||
from libvmgr import util
|
||||
from libvmgr.rwlock import ReadWriteLock
|
||||
from libvirt import libvirtError
|
||||
|
||||
|
||||
CONN_SOCKET = 4
|
||||
CONN_TLS = 3
|
||||
CONN_SSH = 2
|
||||
CONN_TCP = 1
|
||||
TLS_PORT = 16514
|
||||
SSH_PORT = 22
|
||||
TCP_PORT = 16509
|
||||
KEEPALIVE_COUNT = 30
|
||||
KEEPALIVE_INTERVAL = 5
|
||||
|
||||
|
||||
def host_is_up(conn_type, hostname):
|
||||
"""
|
||||
returns True if the given host is up and we are able to establish
|
||||
a connection using the given credentials.
|
||||
"""
|
||||
try:
|
||||
socket_host = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
socket_host.settimeout(2)
|
||||
if conn_type == CONN_SSH:
|
||||
if ':' in hostname:
|
||||
LIBVIRT_HOST, PORT = (hostname).split(":")
|
||||
PORT = int(PORT)
|
||||
else:
|
||||
PORT = SSH_PORT
|
||||
LIBVIRT_HOST = hostname
|
||||
socket_host.connect((LIBVIRT_HOST, PORT))
|
||||
if conn_type == CONN_TCP:
|
||||
socket_host.connect((hostname, TCP_PORT))
|
||||
if conn_type == CONN_TLS:
|
||||
socket_host.connect((hostname, TLS_PORT))
|
||||
socket_host.close()
|
||||
except socket.error:
|
||||
raise libvirtError('Unable to connect to host server: Operation timed out')
|
||||
|
||||
|
||||
class wvcEventLoop(threading.Thread):
|
||||
def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
|
||||
# register the default event implementation
|
||||
# of libvirt, as we do not have an existing
|
||||
# event loop.
|
||||
libvirt.virEventRegisterDefaultImpl()
|
||||
|
||||
if name is None:
|
||||
name = 'libvirt event loop'
|
||||
|
||||
super(wvcEventLoop, self).__init__(group, target, name, args, kwargs)
|
||||
|
||||
# we run this thread in deamon mode, so it does
|
||||
# not block shutdown of the server
|
||||
self.daemon = True
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
# if this method will fail it raises libvirtError
|
||||
# we do not catch the exception here so it will show up
|
||||
# in the logs. Not sure when this call will ever fail
|
||||
libvirt.virEventRunDefaultImpl()
|
||||
|
||||
|
||||
class wvcConnection(object):
|
||||
"""
|
||||
class representing a single connection stored in the Connection Manager
|
||||
# to-do: may also need some locking to ensure to not connect simultaniously in 2 threads
|
||||
"""
|
||||
|
||||
def __init__(self, host, login, passwd, conn):
|
||||
"""
|
||||
Sets all class attributes and tries to open the connection
|
||||
"""
|
||||
# connection lock is used to lock all changes to the connection state attributes
|
||||
# (connection and last_error)
|
||||
self.connection_state_lock = threading.Lock()
|
||||
self.connection = None
|
||||
self.last_error = None
|
||||
|
||||
# credentials
|
||||
self.host = host
|
||||
self.login = login
|
||||
self.passwd = passwd
|
||||
self.type = conn
|
||||
|
||||
# connect
|
||||
self.connect()
|
||||
|
||||
def connect(self):
|
||||
self.connection_state_lock.acquire()
|
||||
try:
|
||||
# recheck if we have a connection (it may have been
|
||||
if not self.connected:
|
||||
if self.type == CONN_TCP:
|
||||
self.__connect_tcp()
|
||||
elif self.type == CONN_SSH:
|
||||
self.__connect_ssh()
|
||||
elif self.type == CONN_TLS:
|
||||
self.__connect_tls()
|
||||
elif self.type == CONN_SOCKET:
|
||||
self.__connect_socket()
|
||||
else:
|
||||
raise ValueError('"{type}" is not a valid connection type'.format(type=self.type))
|
||||
|
||||
if self.connected:
|
||||
# do some preprocessing of the connection:
|
||||
# * set keep alive interval
|
||||
# * set connection close/fail handler
|
||||
try:
|
||||
self.connection.setKeepAlive(connection_manager.keepalive_interval,
|
||||
connection_manager.keepalive_count)
|
||||
try:
|
||||
self.connection.registerCloseCallback(self.__connection_close_callback, None)
|
||||
except Exception:
|
||||
# Temporary fix for libvirt > libvirt-0.10.2-41
|
||||
pass
|
||||
except libvirtError as e:
|
||||
# hypervisor driver does not seem to support persistent connections
|
||||
self.last_error = str(e)
|
||||
finally:
|
||||
self.connection_state_lock.release()
|
||||
|
||||
@property
|
||||
def connected(self):
|
||||
try:
|
||||
return self.connection is not None and self.connection.isAlive()
|
||||
except libvirtError:
|
||||
# isAlive failed for some reason
|
||||
return False
|
||||
|
||||
def __libvirt_auth_credentials_callback(self, credentials, user_data):
|
||||
for credential in credentials:
|
||||
if credential[0] == libvirt.VIR_CRED_AUTHNAME:
|
||||
credential[4] = self.login
|
||||
if len(credential[4]) == 0:
|
||||
credential[4] = credential[3]
|
||||
elif credential[0] == libvirt.VIR_CRED_PASSPHRASE:
|
||||
credential[4] = self.passwd
|
||||
else:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
def __connection_close_callback(self, connection, reason, opaque=None):
|
||||
self.connection_state_lock.acquire()
|
||||
try:
|
||||
# on server shutdown libvirt module gets freed before the close callbacks are called
|
||||
# so we just check here if it is still present
|
||||
if libvirt is not None:
|
||||
self.last_error = 'Connection closed'
|
||||
|
||||
# prevent other threads from using the connection (in the future)
|
||||
self.connection = None
|
||||
finally:
|
||||
self.connection_state_lock.release()
|
||||
|
||||
def __connect_tcp(self):
|
||||
flags = [libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE]
|
||||
auth = [flags, self.__libvirt_auth_credentials_callback, None]
|
||||
uri = 'qemu+tcp://%s/system' % self.host
|
||||
|
||||
try:
|
||||
self.connection = libvirt.openAuth(uri, auth, 0)
|
||||
self.last_error = None
|
||||
|
||||
except libvirtError as e:
|
||||
self.last_error = 'Connection Failed: ' + str(e)
|
||||
self.connection = None
|
||||
|
||||
def __connect_ssh(self):
|
||||
uri = 'qemu+ssh://%s@%s/system' % (self.login, self.host)
|
||||
|
||||
try:
|
||||
self.connection = libvirt.open(uri)
|
||||
self.last_error = None
|
||||
|
||||
except libvirtError as e:
|
||||
self.last_error = 'Connection Failed: ' + str(e) + ' --- ' + repr(libvirt.virGetLastError())
|
||||
self.connection = None
|
||||
|
||||
def __connect_tls(self):
|
||||
flags = [libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE]
|
||||
auth = [flags, self.__libvirt_auth_credentials_callback, None]
|
||||
uri = 'qemu+tls://%s@%s/system' % (self.login, self.host)
|
||||
|
||||
try:
|
||||
self.connection = libvirt.openAuth(uri, auth, 0)
|
||||
self.last_error = None
|
||||
|
||||
except libvirtError as e:
|
||||
self.last_error = 'Connection Failed: ' + str(e)
|
||||
self.connection = None
|
||||
|
||||
def __connect_socket(self):
|
||||
uri = 'qemu:///system'
|
||||
|
||||
try:
|
||||
self.connection = libvirt.open(uri)
|
||||
self.last_error = None
|
||||
|
||||
except libvirtError as e:
|
||||
self.last_error = 'Connection Failed: ' + str(e)
|
||||
self.connection = None
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
closes the connection (if it is active)
|
||||
"""
|
||||
self.connection_state_lock.acquire()
|
||||
try:
|
||||
if self.connected:
|
||||
try:
|
||||
# to-do: handle errors?
|
||||
self.connection.close()
|
||||
except libvirtError:
|
||||
pass
|
||||
|
||||
self.connection = None
|
||||
self.last_error = None
|
||||
finally:
|
||||
self.connection_state_lock.release()
|
||||
|
||||
def __del__(self):
|
||||
if self.connection is not None:
|
||||
# unregister callback (as it is no longer valid if this instance gets deleted)
|
||||
try:
|
||||
self.connection.unregisterCloseCallback()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def __unicode__(self):
|
||||
if self.type == CONN_TCP:
|
||||
type_str = u'tcp'
|
||||
elif self.type == CONN_SSH:
|
||||
type_str = u'ssh'
|
||||
elif self.type == CONN_TLS:
|
||||
type_str = u'tls'
|
||||
else:
|
||||
type_str = u'invalid_type'
|
||||
|
||||
return u'qemu+{type}://{user}@{host}/system'.format(type=type_str, user=self.login, host=self.host)
|
||||
|
||||
def __repr__(self):
|
||||
return '<wvcConnection {connection_str}>'.format(connection_str=str(self))
|
||||
|
||||
|
||||
class wvcConnectionManager(object):
|
||||
def __init__(self, keepalive_interval=5, keepalive_count=5):
|
||||
self.keepalive_interval = keepalive_interval
|
||||
self.keepalive_count = keepalive_count
|
||||
|
||||
# connection dict
|
||||
# maps hostnames to a list of connection objects for this hostname
|
||||
# atm it is possible to create more than one connection per hostname
|
||||
# with different logins or auth methods
|
||||
# connections are shared between all threads, see:
|
||||
# http://wiki.libvirt.org/page/FAQ#Is_libvirt_thread_safe.3F
|
||||
self._connections = dict()
|
||||
self._connections_lock = ReadWriteLock()
|
||||
|
||||
# start event loop to handle keepalive requests and other events
|
||||
self._event_loop = wvcEventLoop()
|
||||
self._event_loop.start()
|
||||
|
||||
def _search_connection(self, host, login, passwd, conn):
|
||||
"""
|
||||
search the connection dict for a connection with the given credentials
|
||||
if it does not exist return None
|
||||
"""
|
||||
self._connections_lock.acquireRead()
|
||||
try:
|
||||
if (host in self._connections):
|
||||
connections = self._connections[host]
|
||||
|
||||
for connection in connections:
|
||||
if (connection.login == login and connection.passwd == passwd and connection.type == conn):
|
||||
return connection
|
||||
finally:
|
||||
self._connections_lock.release()
|
||||
|
||||
return None
|
||||
|
||||
def get_connection(self, host, login, passwd, conn):
|
||||
"""
|
||||
returns a connection object (as returned by the libvirt.open* methods) for the given host and credentials
|
||||
raises libvirtError if (re)connecting fails
|
||||
"""
|
||||
# force all string values to unicode changed for Python3 to str
|
||||
host = str(host)
|
||||
login = str(login)
|
||||
passwd = str(passwd) if passwd is not None else None
|
||||
|
||||
connection = self._search_connection(host, login, passwd, conn)
|
||||
|
||||
if (connection is None):
|
||||
self._connections_lock.acquireWrite()
|
||||
try:
|
||||
# we have to search for the connection again after aquireing the write lock
|
||||
# as the thread previously holding the write lock may have already added our connection
|
||||
connection = self._search_connection(host, login, passwd, conn)
|
||||
if (connection is None):
|
||||
# create a new connection if a matching connection does not already exist
|
||||
connection = wvcConnection(host, login, passwd, conn)
|
||||
|
||||
# add new connection to connection dict
|
||||
if host in self._connections:
|
||||
self._connections[host].append(connection)
|
||||
else:
|
||||
self._connections[host] = [connection]
|
||||
finally:
|
||||
self._connections_lock.release()
|
||||
|
||||
elif not connection.connected:
|
||||
# try to (re-)connect if connection is closed
|
||||
connection.connect()
|
||||
|
||||
if connection.connected:
|
||||
# return libvirt connection object
|
||||
return connection.connection
|
||||
else:
|
||||
# raise libvirt error
|
||||
raise libvirtError(connection.last_error)
|
||||
|
||||
|
||||
connection_manager = wvcConnectionManager(KEEPALIVE_INTERVAL, KEEPALIVE_COUNT)
|
||||
|
||||
|
||||
class wvcConnect(object):
|
||||
def __init__(self, host, login=None, passwd=None, conn_type=CONN_SOCKET, keepalive=True):
|
||||
self.login = login
|
||||
self.host = host
|
||||
self.passwd = passwd
|
||||
self.conn_type = conn_type
|
||||
self.keepalive = keepalive
|
||||
|
||||
# is host up?
|
||||
host_is_up(self.conn_type, self.host)
|
||||
|
||||
# get connection from connection manager
|
||||
if self.keepalive:
|
||||
self.conn = connection_manager.get_connection(host, login, passwd, conn_type)
|
||||
else:
|
||||
if self.conn_type == CONN_TCP:
|
||||
def creds(credentials, user_data):
|
||||
for credential in credentials:
|
||||
if credential[0] == libvirt.VIR_CRED_AUTHNAME:
|
||||
credential[4] = self.login
|
||||
if len(credential[4]) == 0:
|
||||
credential[4] = credential[3]
|
||||
elif credential[0] == libvirt.VIR_CRED_PASSPHRASE:
|
||||
credential[4] = self.passwd
|
||||
else:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
flags = [libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE]
|
||||
auth = [flags, creds, None]
|
||||
uri = 'qemu+tcp://%s/system' % self.host
|
||||
try:
|
||||
self.conn = libvirt.openAuth(uri, auth, 0)
|
||||
except libvirtError:
|
||||
raise libvirtError('Connection Failed')
|
||||
|
||||
if self.conn_type == CONN_SSH:
|
||||
uri = 'qemu+ssh://%s@%s/system' % (self.login, self.host)
|
||||
try:
|
||||
self.conn = libvirt.open(uri)
|
||||
except libvirtError as err:
|
||||
raise err
|
||||
|
||||
if self.conn_type == CONN_TLS:
|
||||
def creds(credentials, user_data):
|
||||
for credential in credentials:
|
||||
if credential[0] == libvirt.VIR_CRED_AUTHNAME:
|
||||
credential[4] = self.login
|
||||
if len(credential[4]) == 0:
|
||||
credential[4] = credential[3]
|
||||
elif credential[0] == libvirt.VIR_CRED_PASSPHRASE:
|
||||
credential[4] = self.passwd
|
||||
else:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
flags = [libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE]
|
||||
auth = [flags, creds, None]
|
||||
uri = 'qemu+tls://%s@%s/system' % (self.login, self.host)
|
||||
try:
|
||||
self.conn = libvirt.openAuth(uri, auth, 0)
|
||||
except libvirtError:
|
||||
raise libvirtError('Connection Failed')
|
||||
|
||||
def get_cap_xml(self):
|
||||
"""Return xml capabilities"""
|
||||
return self.conn.getCapabilities()
|
||||
|
||||
def is_kvm_supported(self):
|
||||
"""Return KVM capabilities."""
|
||||
return util.is_kvm_available(self.get_cap_xml())
|
||||
|
||||
def get_storages(self):
|
||||
storages = []
|
||||
for pool in self.conn.listStoragePools():
|
||||
storages.append(pool)
|
||||
for pool in self.conn.listDefinedStoragePools():
|
||||
storages.append(pool)
|
||||
return storages
|
||||
|
||||
def get_networks(self):
|
||||
virtnet = []
|
||||
for net in self.conn.listNetworks():
|
||||
virtnet.append(net)
|
||||
for net in self.conn.listDefinedNetworks():
|
||||
virtnet.append(net)
|
||||
return virtnet
|
||||
|
||||
def get_ifaces(self):
|
||||
interface = []
|
||||
for inface in self.conn.listInterfaces():
|
||||
interface.append(inface)
|
||||
for inface in self.conn.listDefinedInterfaces():
|
||||
interface.append(inface)
|
||||
return interface
|
||||
|
||||
def get_iface(self, name):
|
||||
return self.conn.interfaceLookupByName(name)
|
||||
|
||||
def get_secrets(self):
|
||||
return self.conn.listSecrets()
|
||||
|
||||
def get_secret(self, uuid):
|
||||
return self.conn.secretLookupByUUIDString(uuid)
|
||||
|
||||
def get_storage(self, name):
|
||||
return self.conn.storagePoolLookupByName(name)
|
||||
|
||||
def get_volume_by_path(self, path):
|
||||
return self.conn.storageVolLookupByPath(path)
|
||||
|
||||
def get_network(self, net):
|
||||
return self.conn.networkLookupByName(net)
|
||||
|
||||
def get_instance(self, name):
|
||||
return self.conn.lookupByName(name)
|
||||
|
||||
def get_instance_status(self, name):
|
||||
dom = self.conn.lookupByName(name)
|
||||
return dom.info()[0]
|
||||
|
||||
def get_instances(self):
|
||||
instances = []
|
||||
for inst_id in self.conn.listDomainsID():
|
||||
dom = self.conn.lookupByID(int(inst_id))
|
||||
instances.append(dom.name())
|
||||
for name in self.conn.listDefinedDomains():
|
||||
instances.append(name)
|
||||
return instances
|
||||
|
||||
def get_snapshots(self):
|
||||
instance = []
|
||||
for snap_id in self.conn.listDomainsID():
|
||||
dom = self.conn.lookupByID(int(snap_id))
|
||||
if dom.snapshotNum(0) != 0:
|
||||
instance.append(dom.name())
|
||||
for name in self.conn.listDefinedDomains():
|
||||
dom = self.conn.lookupByName(name)
|
||||
if dom.snapshotNum(0) != 0:
|
||||
instance.append(dom.name())
|
||||
return instance
|
||||
|
||||
def get_net_device(self):
|
||||
netdevice = []
|
||||
for dev in self.conn.listAllDevices(0):
|
||||
xml = dev.XMLDesc(0)
|
||||
if util.get_xml_data(xml, 'capability', 'type') == 'net':
|
||||
netdevice.append(util.get_xml_data(xml, 'capability/interface'))
|
||||
return netdevice
|
||||
|
||||
def get_host_instances(self):
|
||||
vname = {}
|
||||
for name in self.get_instances():
|
||||
dom = self.get_instance(name)
|
||||
mem = util.get_xml_data(dom.XMLDesc(0), 'currentMemory')
|
||||
mem = round(int(mem) / 1024)
|
||||
cur_vcpu = util.get_xml_data(dom.XMLDesc(0), 'vcpu', 'current')
|
||||
if cur_vcpu:
|
||||
vcpu = cur_vcpu
|
||||
else:
|
||||
vcpu = util.get_xml_data(dom.XMLDesc(0), 'vcpu')
|
||||
vname[dom.name()] = {'status': dom.info()[0], 'uuid': dom.UUIDString(), 'vcpu': vcpu, 'memory': mem}
|
||||
return vname
|
||||
|
||||
def close(self):
|
||||
"""Close connection"""
|
||||
if not self.keepalive:
|
||||
self.conn.close()
|
||||
81
backend/libvmgr/host.py
Normal file
81
backend/libvmgr/host.py
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import time
|
||||
from libvmgr import util
|
||||
from libvmgr.connect import wvcConnect
|
||||
|
||||
|
||||
class wvcHost(wvcConnect):
|
||||
def __init__(self, conn):
|
||||
self.conn = conn
|
||||
|
||||
def get_node_info(self):
|
||||
"""
|
||||
Function return host server information: hostname, cpu, memory, ...
|
||||
"""
|
||||
info = list()
|
||||
info.append(self.conn.getHostname())
|
||||
info.append(self.conn.getInfo()[0])
|
||||
info.append(self.conn.getInfo()[1] * (1024**2))
|
||||
info.append(self.conn.getInfo()[2])
|
||||
info.append(util.get_xml_data(self.conn.getSysinfo(0), 'processor/entry[6]'))
|
||||
info.append(self.conn.getURI())
|
||||
return info
|
||||
|
||||
def hypervisor_type(self):
|
||||
"""
|
||||
Return hypervisor type
|
||||
"""
|
||||
return util.get_xml_data(self.get_cap_xml(), 'guest/arch/domain', 'type')
|
||||
|
||||
def get_memory_usage(self):
|
||||
"""
|
||||
Function return memory usage on node.
|
||||
"""
|
||||
host_mem = self.conn.getInfo()[1] * (1024**2)
|
||||
free_mem = self.conn.getMemoryStats(-1, 0)
|
||||
if isinstance(free_mem, dict):
|
||||
mem = list(free_mem.values())
|
||||
free = (mem[1] + mem[2] + mem[3]) * 1024
|
||||
percent = (100 - ((free * 100) / host_mem))
|
||||
usage = (host_mem - free)
|
||||
mem_usage = {'size': host_mem, 'usage': usage, 'percent': round(percent)}
|
||||
else:
|
||||
mem_usage = {'size': 0, 'usage': 0, 'percent': 0}
|
||||
return mem_usage
|
||||
|
||||
def get_storage_usage(self, name):
|
||||
"""
|
||||
Function return storage usage on node by name.
|
||||
"""
|
||||
pool = self.get_storage(name)
|
||||
pool.refresh()
|
||||
if pool.isActive():
|
||||
size = pool.info()[1]
|
||||
free = pool.info()[3]
|
||||
used = size - free
|
||||
percent = (used * 100) / size
|
||||
return {'size': size, 'used': used, 'percent': percent}
|
||||
else:
|
||||
return {'size': 0, 'used': 0, 'percent': 0}
|
||||
|
||||
def get_cpu_usage(self):
|
||||
"""
|
||||
Function return cpu usage on node.
|
||||
"""
|
||||
prev_idle = 0
|
||||
prev_total = 0
|
||||
diff_usage = 0
|
||||
cpu = self.conn.getCPUStats(-1, 0)
|
||||
if isinstance(cpu, dict):
|
||||
for num in range(2):
|
||||
idle = self.conn.getCPUStats(-1, 0)['idle']
|
||||
total = sum(self.conn.getCPUStats(-1, 0).values())
|
||||
diff_idle = idle - prev_idle
|
||||
diff_total = total - prev_total
|
||||
diff_usage = (1000 * (diff_total - diff_idle) / diff_total + 5) / 10
|
||||
prev_total = total
|
||||
prev_idle = idle
|
||||
if num == 0:
|
||||
time.sleep(1)
|
||||
if diff_usage < 0:
|
||||
diff_usage = 0
|
||||
return {'usage': round(diff_usage)}
|
||||
226
backend/libvmgr/rwlock.py
Normal file
226
backend/libvmgr/rwlock.py
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
# -*- coding: iso-8859-15 -*-
|
||||
"""locks.py - Read-Write lock thread lock implementation
|
||||
|
||||
See the class documentation for more info.
|
||||
|
||||
Copyright (C) 2007, Heiko Wundram.
|
||||
Released under the BSD-license.
|
||||
|
||||
found at: http://code.activestate.com/recipes/502283-read-write-lock-class-rlock-like/
|
||||
"""
|
||||
|
||||
# Imports
|
||||
# -------
|
||||
|
||||
from threading import Condition, Lock, currentThread
|
||||
from time import time
|
||||
|
||||
|
||||
# Read write lock
|
||||
# ---------------
|
||||
|
||||
class ReadWriteLock(object):
|
||||
"""Read-Write lock class. A read-write lock differs from a standard
|
||||
threading.RLock() by allowing multiple threads to simultaneously hold a
|
||||
read lock, while allowing only a single thread to hold a write lock at the
|
||||
same point of time.
|
||||
|
||||
When a read lock is requested while a write lock is held, the reader
|
||||
is blocked; when a write lock is requested while another write lock is
|
||||
held or there are read locks, the writer is blocked.
|
||||
|
||||
Writers are always preferred by this implementation: if there are blocked
|
||||
threads waiting for a write lock, current readers may request more read
|
||||
locks (which they eventually should free, as they starve the waiting
|
||||
writers otherwise), but a new thread requesting a read lock will not
|
||||
be granted one, and block. This might mean starvation for readers if
|
||||
two writer threads interweave their calls to acquireWrite() without
|
||||
leaving a window only for readers.
|
||||
|
||||
In case a current reader requests a write lock, this can and will be
|
||||
satisfied without giving up the read locks first, but, only one thread
|
||||
may perform this kind of lock upgrade, as a deadlock would otherwise
|
||||
occur. After the write lock has been granted, the thread will hold a
|
||||
full write lock, and not be downgraded after the upgrading call to
|
||||
acquireWrite() has been match by a corresponding release().
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize this read-write lock."""
|
||||
|
||||
# Condition variable, used to signal waiters of a change in object
|
||||
# state.
|
||||
self.__condition = Condition(Lock())
|
||||
|
||||
# Initialize with no writers.
|
||||
self.__writer = None
|
||||
self.__upgradewritercount = 0
|
||||
self.__pendingwriters = []
|
||||
|
||||
# Initialize with no readers.
|
||||
self.__readers = {}
|
||||
|
||||
def acquireRead(self, timeout=None):
|
||||
"""Acquire a read lock for the current thread, waiting at most
|
||||
timeout seconds or doing a non-blocking check in case timeout is <= 0.
|
||||
|
||||
In case timeout is None, the call to acquireRead blocks until the
|
||||
lock request can be serviced.
|
||||
|
||||
In case the timeout expires before the lock could be serviced, a
|
||||
RuntimeError is thrown."""
|
||||
|
||||
if timeout is not None:
|
||||
endtime = time() + timeout
|
||||
me = currentThread()
|
||||
self.__condition.acquire()
|
||||
try:
|
||||
if self.__writer is me:
|
||||
# If we are the writer, grant a new read lock, always.
|
||||
self.__writercount += 1
|
||||
return
|
||||
while True:
|
||||
if self.__writer is None:
|
||||
# Only test anything if there is no current writer.
|
||||
if self.__upgradewritercount or self.__pendingwriters:
|
||||
if me in self.__readers:
|
||||
# Only grant a read lock if we already have one
|
||||
# in case writers are waiting for their turn.
|
||||
# This means that writers can't easily get starved
|
||||
# (but see below, readers can).
|
||||
self.__readers[me] += 1
|
||||
return
|
||||
# No, we aren't a reader (yet), wait for our turn.
|
||||
else:
|
||||
# Grant a new read lock, always, in case there are
|
||||
# no pending writers (and no writer).
|
||||
self.__readers[me] = self.__readers.get(me, 0) + 1
|
||||
return
|
||||
if timeout is not None:
|
||||
remaining = endtime - time()
|
||||
if remaining <= 0:
|
||||
# Timeout has expired, signal caller of this.
|
||||
raise RuntimeError("Acquiring read lock timed out")
|
||||
self.__condition.wait(remaining)
|
||||
else:
|
||||
self.__condition.wait()
|
||||
finally:
|
||||
self.__condition.release()
|
||||
|
||||
def acquireWrite(self, timeout=None):
|
||||
"""Acquire a write lock for the current thread, waiting at most
|
||||
timeout seconds or doing a non-blocking check in case timeout is <= 0.
|
||||
|
||||
In case the write lock cannot be serviced due to the deadlock
|
||||
condition mentioned above, a ValueError is raised.
|
||||
|
||||
In case timeout is None, the call to acquireWrite blocks until the
|
||||
lock request can be serviced.
|
||||
|
||||
In case the timeout expires before the lock could be serviced, a
|
||||
RuntimeError is thrown."""
|
||||
|
||||
if timeout is not None:
|
||||
endtime = time() + timeout
|
||||
me, upgradewriter = currentThread(), False
|
||||
self.__condition.acquire()
|
||||
try:
|
||||
if self.__writer is me:
|
||||
# If we are the writer, grant a new write lock, always.
|
||||
self.__writercount += 1
|
||||
return
|
||||
elif me in self.__readers:
|
||||
# If we are a reader, no need to add us to pendingwriters,
|
||||
# we get the upgradewriter slot.
|
||||
if self.__upgradewritercount:
|
||||
# If we are a reader and want to upgrade, and someone
|
||||
# else also wants to upgrade, there is no way we can do
|
||||
# this except if one of us releases all his read locks.
|
||||
# Signal this to user.
|
||||
raise ValueError(
|
||||
"Inevitable dead lock, denying write lock"
|
||||
)
|
||||
upgradewriter = True
|
||||
self.__upgradewritercount = self.__readers.pop(me)
|
||||
else:
|
||||
# We aren't a reader, so add us to the pending writers queue
|
||||
# for synchronization with the readers.
|
||||
self.__pendingwriters.append(me)
|
||||
while True:
|
||||
if not self.__readers and self.__writer is None:
|
||||
# Only test anything if there are no readers and writers.
|
||||
if self.__upgradewritercount:
|
||||
if upgradewriter:
|
||||
# There is a writer to upgrade, and it's us. Take
|
||||
# the write lock.
|
||||
self.__writer = me
|
||||
self.__writercount = self.__upgradewritercount + 1
|
||||
self.__upgradewritercount = 0
|
||||
return
|
||||
# There is a writer to upgrade, but it's not us.
|
||||
# Always leave the upgrade writer the advance slot,
|
||||
# because he presumes he'll get a write lock directly
|
||||
# from a previously held read lock.
|
||||
elif self.__pendingwriters[0] is me:
|
||||
# If there are no readers and writers, it's always
|
||||
# fine for us to take the writer slot, removing us
|
||||
# from the pending writers queue.
|
||||
# This might mean starvation for readers, though.
|
||||
self.__writer = me
|
||||
self.__writercount = 1
|
||||
self.__pendingwriters = self.__pendingwriters[1:]
|
||||
return
|
||||
if timeout is not None:
|
||||
remaining = endtime - time()
|
||||
if remaining <= 0:
|
||||
# Timeout has expired, signal caller of this.
|
||||
if upgradewriter:
|
||||
# Put us back on the reader queue. No need to
|
||||
# signal anyone of this change, because no other
|
||||
# writer could've taken our spot before we got
|
||||
# here (because of remaining readers), as the test
|
||||
# for proper conditions is at the start of the
|
||||
# loop, not at the end.
|
||||
self.__readers[me] = self.__upgradewritercount
|
||||
self.__upgradewritercount = 0
|
||||
else:
|
||||
# We were a simple pending writer, just remove us
|
||||
# from the FIFO list.
|
||||
self.__pendingwriters.remove(me)
|
||||
raise RuntimeError("Acquiring write lock timed out")
|
||||
self.__condition.wait(remaining)
|
||||
else:
|
||||
self.__condition.wait()
|
||||
finally:
|
||||
self.__condition.release()
|
||||
|
||||
def release(self):
|
||||
"""Release the currently held lock.
|
||||
|
||||
In case the current thread holds no lock, a ValueError is thrown."""
|
||||
|
||||
me = currentThread()
|
||||
self.__condition.acquire()
|
||||
try:
|
||||
if self.__writer is me:
|
||||
# We are the writer, take one nesting depth away.
|
||||
self.__writercount -= 1
|
||||
if not self.__writercount:
|
||||
# No more write locks; take our writer position away and
|
||||
# notify waiters of the new circumstances.
|
||||
self.__writer = None
|
||||
self.__condition.notifyAll()
|
||||
elif me in self.__readers:
|
||||
# We are a reader currently, take one nesting depth away.
|
||||
self.__readers[me] -= 1
|
||||
if not self.__readers[me]:
|
||||
# No more read locks, take our reader position away.
|
||||
del self.__readers[me]
|
||||
if not self.__readers:
|
||||
# No more readers, notify waiters of the new
|
||||
# circumstances.
|
||||
self.__condition.notifyAll()
|
||||
else:
|
||||
raise ValueError("Trying to release unheld lock")
|
||||
finally:
|
||||
self.__condition.release()
|
||||
159
backend/libvmgr/util.py
Normal file
159
backend/libvmgr/util.py
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
import re
|
||||
import socket
|
||||
import random
|
||||
import libvirt
|
||||
import paramiko
|
||||
from time import sleep
|
||||
from xml.etree import ElementTree
|
||||
from string import ascii_letters, digits
|
||||
from passlib.hash import sha512_crypt
|
||||
|
||||
|
||||
def is_kvm_available(xml):
|
||||
tree = ElementTree.fromstring(xml)
|
||||
for dom in tree.findall('guest/arch/domain'):
|
||||
if 'kvm' in dom.get('type'):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def randomMAC():
|
||||
"""Generate a random MAC address."""
|
||||
# qemu MAC
|
||||
oui = [0x52, 0x54, 0x00]
|
||||
|
||||
mac = oui + [random.randint(0x00, 0xff),
|
||||
random.randint(0x00, 0xff),
|
||||
random.randint(0x00, 0xff)]
|
||||
return ':'.join(map(lambda x: "%02x" % x, mac))
|
||||
|
||||
|
||||
def randomUUID():
|
||||
"""Generate a random UUID."""
|
||||
|
||||
u = [random.randint(0, 255) for dummy in range(0, 16)]
|
||||
return "-".join(["%02x" * 4, "%02x" * 2, "%02x" * 2, "%02x" * 2, "%02x" * 6]) % tuple(u)
|
||||
|
||||
|
||||
def get_max_vcpus(conn, type=None):
|
||||
"""@param conn: libvirt connection to poll for max possible vcpus
|
||||
@type type: optional guest type (kvm, etc.)"""
|
||||
if type is None:
|
||||
type = conn.getType()
|
||||
try:
|
||||
m = conn.getMaxVcpus(type.lower())
|
||||
except libvirt.libvirtError:
|
||||
m = 32
|
||||
return m
|
||||
|
||||
|
||||
def xml_escape(str):
|
||||
"""Replaces chars ' " < > & with xml safe counterparts"""
|
||||
if str is None:
|
||||
return None
|
||||
|
||||
str = str.replace("&", "&")
|
||||
str = str.replace("'", "'")
|
||||
str = str.replace("\"", """)
|
||||
str = str.replace("<", "<")
|
||||
str = str.replace(">", ">")
|
||||
return str
|
||||
|
||||
|
||||
def compareMAC(p, q):
|
||||
"""Compare two MAC addresses"""
|
||||
pa = p.split(":")
|
||||
qa = q.split(":")
|
||||
|
||||
if len(pa) != len(qa):
|
||||
if p > q:
|
||||
return 1
|
||||
else:
|
||||
return -1
|
||||
|
||||
for i in range(len(pa)):
|
||||
n = int(pa[i], 0x10) - int(qa[i], 0x10)
|
||||
if n > 0:
|
||||
return 1
|
||||
elif n < 0:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
|
||||
def get_xml_data(xml, path=None, element=None):
|
||||
res = ''
|
||||
if not path and not element:
|
||||
return ''
|
||||
|
||||
tree = ElementTree.fromstring(xml)
|
||||
if path:
|
||||
child = tree.find(path)
|
||||
if child is not None:
|
||||
if element:
|
||||
res = child.get(element)
|
||||
else:
|
||||
res = child.text
|
||||
else:
|
||||
res = tree.get(element)
|
||||
return res
|
||||
|
||||
|
||||
def get_xml_findall(xml, string):
|
||||
tree = ElementTree.fromstring(xml)
|
||||
return tree.findall(string)
|
||||
|
||||
|
||||
def pretty_mem(val):
|
||||
val = int(val)
|
||||
if val > (10 * 1024 * 1024):
|
||||
return "%2.2f GB" % (val / (1024.0 * 1024.0))
|
||||
else:
|
||||
return "%2.0f MB" % (val / 1024.0)
|
||||
|
||||
|
||||
def pretty_bytes(val):
|
||||
val = int(val)
|
||||
if val > (1024 * 1024 * 1024):
|
||||
return "%2.2f GB" % (val / (1024.0 * 1024.0 * 1024.0))
|
||||
else:
|
||||
return "%2.2f MB" % (val / (1024.0 * 1024.0))
|
||||
|
||||
|
||||
def gen_password(length=14):
|
||||
password = ''.join(
|
||||
[random.choice(ascii_letters + digits) for dummy in range(length)]
|
||||
)
|
||||
return password
|
||||
|
||||
|
||||
def password_to_hash(password):
|
||||
salt = gen_password(8)
|
||||
password_hash = sha512_crypt.encrypt(password, salt=salt, rounds=5000)
|
||||
return password_hash
|
||||
|
||||
|
||||
def similar_name(pattern, names):
|
||||
res = []
|
||||
for name in names:
|
||||
match = re.match(pattern, name)
|
||||
if match:
|
||||
res.append(name)
|
||||
return res
|
||||
|
||||
|
||||
def check_ssh_connection(hostname, password, username='root', timeout=90):
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
|
||||
for i in range(timeout):
|
||||
try:
|
||||
ssh.connect(hostname, username=username, password=password)
|
||||
ssh.close()
|
||||
return True
|
||||
except (paramiko.BadHostKeyException,
|
||||
paramiko.AuthenticationException,
|
||||
paramiko.SSHException,
|
||||
socket.error,
|
||||
EOFError):
|
||||
sleep(1)
|
||||
return False
|
||||
Loading…
Add table
Add a link
Reference in a new issue