Formating & fixing type checkings

This commit is contained in:
Adolfo Gómez García
2021-08-14 15:47:21 +02:00
parent 8285e2daad
commit afcbd058d1
36 changed files with 1136 additions and 409 deletions

View File

@ -49,6 +49,7 @@ logger = logging.getLogger(__name__)
lock = threading.Lock() lock = threading.Lock()
class Client: class Client:
""" """
Module to manage oVirt connections using ovirtsdk. Module to manage oVirt connections using ovirtsdk.
@ -59,11 +60,14 @@ class Client:
This can waste a lot of time, so use of cache here is more than important to achieve aceptable performance. This can waste a lot of time, so use of cache here is more than important to achieve aceptable performance.
""" """
cached_api: typing.ClassVar[typing.Optional[ovirt.Connection]] = None cached_api: typing.ClassVar[typing.Optional[ovirt.Connection]] = None
cached_api_key: typing.ClassVar[typing.Optional[str]] = None cached_api_key: typing.ClassVar[typing.Optional[str]] = None
CACHE_TIME_LOW = 60 * 5 # Cache time for requests are 5 minutes by default CACHE_TIME_LOW = 60 * 5 # Cache time for requests are 5 minutes by default
CACHE_TIME_HIGH = 60 * 30 # Cache time for requests that are less probable to change (as cluster perteinance of a machine) CACHE_TIME_HIGH = (
60 * 30
) # Cache time for requests that are less probable to change (as cluster perteinance of a machine)
_host: str _host: str
_username: str _username: str
@ -79,7 +83,9 @@ class Client:
Returns: Returns:
The cache key, taking into consideration the prefix The cache key, taking into consideration the prefix
""" """
return "{}{}{}{}{}".format(prefix, self._host, self._username, self._password, self._timeout) return "{}{}{}{}{}".format(
prefix, self._host, self._username, self._password, self._timeout
)
def __getApi(self) -> ovirt.Connection: def __getApi(self) -> ovirt.Connection:
""" """
@ -102,7 +108,13 @@ class Client:
pass pass
try: try:
Client.cached_api_key = aKey Client.cached_api_key = aKey
Client.cached_api = ovirt.Connection(url='https://' + self._host + '/ovirt-engine/api', username=self._username, password=self._password, timeout=self._timeout, insecure=True) # , debug=True, log=logger ) Client.cached_api = ovirt.Connection(
url='https://' + self._host + '/ovirt-engine/api',
username=self._username,
password=self._password,
timeout=self._timeout,
insecure=True,
) # , debug=True, log=logger )
return Client.cached_api return Client.cached_api
except: except:
@ -111,7 +123,14 @@ class Client:
Client.cached_api_key = None Client.cached_api_key = None
raise Exception("Can't connet to server at {}".format(self._host)) raise Exception("Can't connet to server at {}".format(self._host))
def __init__(self, host: str, username: str, password: str, timeout: typing.Union[str, int], cache: 'Cache'): def __init__(
self,
host: str,
username: str,
password: str,
timeout: typing.Union[str, int],
cache: 'Cache',
):
self._host = host self._host = host
self._username = username self._username = username
self._password = password self._password = password
@ -135,7 +154,9 @@ class Client:
""" """
return True, 'Test successfully passed' return True, 'Test successfully passed'
def getVms(self, force: bool = False) -> typing.List[typing.MutableMapping[str, typing.Any]]: def getVms(
self, force: bool = False
) -> typing.List[typing.MutableMapping[str, typing.Any]]:
""" """
Obtains the list of machines inside ovirt that do aren't part of uds Obtains the list of machines inside ovirt that do aren't part of uds
@ -161,7 +182,7 @@ class Client:
api = self.__getApi() api = self.__getApi()
vms: typing.Iterable[typing.Any] = api.system_service().vms_service().list() vms: typing.Iterable[typing.Any] = api.system_service().vms_service().list() # type: ignore
logger.debug('oVirt VMS: %s', vms) logger.debug('oVirt VMS: %s', vms)
@ -172,7 +193,14 @@ class Client:
pair = [vm.usb.enabled, vm.usb.type.value] pair = [vm.usb.enabled, vm.usb.type.value]
except Exception: except Exception:
pair = [False, ''] pair = [False, '']
res.append({'name': vm.name, 'id': vm.id, 'cluster_id': vm.cluster.id, 'usb': pair}) res.append(
{
'name': vm.name,
'id': vm.id,
'cluster_id': vm.cluster.id,
'usb': pair,
}
)
self._cache.put(vmsKey, res, Client.CACHE_TIME_LOW) self._cache.put(vmsKey, res, Client.CACHE_TIME_LOW)
@ -181,7 +209,9 @@ class Client:
finally: finally:
lock.release() lock.release()
def getClusters(self, force: bool = False) -> typing.List[typing.MutableMapping[str, typing.Any]]: def getClusters(
self, force: bool = False
) -> typing.List[typing.MutableMapping[str, typing.Any]]:
""" """
Obtains the list of clusters inside ovirt Obtains the list of clusters inside ovirt
@ -208,7 +238,7 @@ class Client:
api = self.__getApi() api = self.__getApi()
clusters = api.system_service().clusters_service().list() clusters: typing.List[typing.Any] = api.system_service().clusters_service().list() # type: ignore
res: typing.List[typing.MutableMapping[str, typing.Any]] = [] res: typing.List[typing.MutableMapping[str, typing.Any]] = []
@ -216,7 +246,11 @@ class Client:
for cluster in clusters: for cluster in clusters:
dc = cluster.data_center dc = cluster.data_center
val = {'name': cluster.name, 'id': cluster.id, 'datacenter_id': dc.id if dc else None} val = {
'name': cluster.name,
'id': cluster.id,
'datacenter_id': dc.id if dc else None,
}
# Updates cache info for every single cluster # Updates cache info for every single cluster
clKey = self.__getKey('o-cluster' + cluster.id) clKey = self.__getKey('o-cluster' + cluster.id)
@ -232,7 +266,9 @@ class Client:
finally: finally:
lock.release() lock.release()
def getClusterInfo(self, clusterId: str, force: bool = False) -> typing.MutableMapping[str, typing.Any]: def getClusterInfo(
self, clusterId: str, force: bool = False
) -> typing.MutableMapping[str, typing.Any]:
""" """
Obtains the cluster info Obtains the cluster info
@ -259,7 +295,7 @@ class Client:
api = self.__getApi() api = self.__getApi()
c = api.system_service().clusters_service().service(clusterId).get() c: typing.Any = api.system_service().clusters_service().service(clusterId).get() # type: ignore
dc = c.data_center dc = c.data_center
@ -272,7 +308,9 @@ class Client:
finally: finally:
lock.release() lock.release()
def getDatacenterInfo(self, datacenterId: str, force: bool = False) -> typing.MutableMapping[str, typing.Any]: def getDatacenterInfo(
self, datacenterId: str, force: bool = False
) -> typing.MutableMapping[str, typing.Any]:
""" """
Obtains the datacenter info Obtains the datacenter info
@ -308,26 +346,35 @@ class Client:
api = self.__getApi() api = self.__getApi()
datacenter_service = api.system_service().data_centers_service().service(datacenterId) datacenter_service = (
d = datacenter_service.get() api.system_service().data_centers_service().service(datacenterId)
)
d: typing.Any = datacenter_service.get() # type: ignore
storage = [] storage = []
for dd in datacenter_service.storage_domains_service().list(): for dd in typing.cast(typing.Iterable, datacenter_service.storage_domains_service().list()): # type: ignore
try: try:
active = dd.status.value active = dd.status.value
except Exception: except Exception:
active = 'inactive' active = 'inactive'
storage.append({'id': dd.id, 'name': dd.name, 'type': dd.type.value, storage.append(
'available': dd.available, 'used': dd.used, {
'active': active == 'active'}) 'id': dd.id,
'name': dd.name,
'type': dd.type.value,
'available': dd.available,
'used': dd.used,
'active': active == 'active',
}
)
res = { res = {
'name': d.name, 'name': d.name,
'id': d.id, 'id': d.id,
'storage_type': d.local and 'local' or 'shared', 'storage_type': d.local and 'local' or 'shared',
'description': d.description, 'description': d.description,
'storage': storage 'storage': storage,
} }
self._cache.put(dcKey, res, Client.CACHE_TIME_HIGH) self._cache.put(dcKey, res, Client.CACHE_TIME_HIGH)
@ -335,7 +382,9 @@ class Client:
finally: finally:
lock.release() lock.release()
def getStorageInfo(self, storageId: str, force: bool = False) -> typing.MutableMapping[str, typing.Any]: def getStorageInfo(
self, storageId: str, force: bool = False
) -> typing.MutableMapping[str, typing.Any]:
""" """
Obtains the datacenter info Obtains the datacenter info
@ -366,14 +415,14 @@ class Client:
api = self.__getApi() api = self.__getApi()
dd = api.system_service().storage_domains_service().service(storageId).get() dd: typing.Any = api.system_service().storage_domains_service().service(storageId).get() # type: ignore
res = { res = {
'id': dd.id, 'id': dd.id,
'name': dd.name, 'name': dd.name,
'type': dd.type.value, 'type': dd.type.value,
'available': dd.available, 'available': dd.available,
'used': dd.used 'used': dd.used,
} }
self._cache.put(sdKey, res, Client.CACHE_TIME_LOW) self._cache.put(sdKey, res, Client.CACHE_TIME_LOW)
@ -388,7 +437,7 @@ class Client:
machineId: str, machineId: str,
clusterId: str, clusterId: str,
storageId: str, storageId: str,
displayType: str displayType: str,
) -> str: ) -> str:
""" """
Publish the machine (makes a template from it so we can create COWs) and returns the template id of Publish the machine (makes a template from it so we can create COWs) and returns the template id of
@ -404,7 +453,15 @@ class Client:
Returns Returns
Raises an exception if operation could not be acomplished, or returns the id of the template being created. Raises an exception if operation could not be acomplished, or returns the id of the template being created.
""" """
logger.debug("n: %s, c: %s, vm: %s, cl: %s, st: %s, dt: %s", name, comments, machineId, clusterId, storageId, displayType) logger.debug(
"n: %s, c: %s, vm: %s, cl: %s, st: %s, dt: %s",
name,
comments,
machineId,
clusterId,
storageId,
displayType,
)
try: try:
lock.acquire(True) lock.acquire(True)
@ -416,8 +473,8 @@ class Client:
vms = api.system_service().vms_service().service(machineId) vms = api.system_service().vms_service().service(machineId)
cluster = api.system_service().clusters_service().service(clusterId).get() cluster: typing.Any = api.system_service().clusters_service().service(clusterId).get() # type: ignore
vm = vms.get() vm: typing.Any = vms.get() # type: ignore
if vm is None: if vm is None:
raise Exception('Machine not found') raise Exception('Machine not found')
@ -439,15 +496,12 @@ class Client:
tcluster = ovirt.types.Cluster(id=cluster.id) tcluster = ovirt.types.Cluster(id=cluster.id)
template = ovirt.types.Template( template = ovirt.types.Template(
name=name, name=name, vm=tvm, cluster=tcluster, description=comments
vm=tvm,
cluster=tcluster,
description=comments
) )
# display=display) # display=display)
return api.system_service().templates_service().add(template).id return api.system_service().templates_service().add(template).id # type: ignore
finally: finally:
lock.release() lock.release()
@ -469,7 +523,9 @@ class Client:
api = self.__getApi() api = self.__getApi()
try: try:
template = api.system_service().templates_service().service(templateId).get() template: typing.Any = (
api.system_service().templates_service().service(templateId).get() # type: ignore
)
if not template: if not template:
return 'removed' return 'removed'
@ -490,7 +546,7 @@ class Client:
displayType: str, displayType: str,
usbType: str, usbType: str,
memoryMB: int, memoryMB: int,
guaranteedMB: int guaranteedMB: int,
) -> str: ) -> str:
""" """
Deploys a virtual machine on selected cluster from selected template Deploys a virtual machine on selected cluster from selected template
@ -507,8 +563,16 @@ class Client:
Returns: Returns:
Id of the machine being created form template Id of the machine being created form template
""" """
logger.debug('Deploying machine with name "%s" from template %s at cluster %s with display %s and usb %s, memory %s and guaranteed %s', logger.debug(
name, templateId, clusterId, displayType, usbType, memoryMB, guaranteedMB) 'Deploying machine with name "%s" from template %s at cluster %s with display %s and usb %s, memory %s and guaranteed %s',
name,
templateId,
clusterId,
displayType,
usbType,
memoryMB,
guaranteedMB,
)
try: try:
lock.acquire(True) lock.acquire(True)
@ -519,18 +583,28 @@ class Client:
cluster = ovirt.types.Cluster(id=clusterId) cluster = ovirt.types.Cluster(id=clusterId)
template = ovirt.types.Template(id=templateId) template = ovirt.types.Template(id=templateId)
if self._needsUsbFix is False and usbType in ('native',): # Removed 'legacy', from 3.6 is not used anymore, and from 4.0 not available if self._needsUsbFix is False and usbType in (
'native',
): # Removed 'legacy', from 3.6 is not used anymore, and from 4.0 not available
usb = ovirt.types.Usb(enabled=True, type=ovirt.types.UsbType.NATIVE) usb = ovirt.types.Usb(enabled=True, type=ovirt.types.UsbType.NATIVE)
else: else:
usb = ovirt.types.Usb(enabled=False) usb = ovirt.types.Usb(enabled=False)
memoryPolicy = ovirt.types.MemoryPolicy(guaranteed=guaranteedMB * 1024 * 1024) memoryPolicy = ovirt.types.MemoryPolicy(
guaranteed=guaranteedMB * 1024 * 1024
)
par = ovirt.types.Vm( par = ovirt.types.Vm(
name=name, cluster=cluster, template=template, description=comments, name=name,
type=ovirt.types.VmType.DESKTOP, memory=memoryMB * 1024 * 1024, memory_policy=memoryPolicy, cluster=cluster,
usb=usb) # display=display, template=template,
description=comments,
type=ovirt.types.VmType.DESKTOP,
memory=memoryMB * 1024 * 1024,
memory_policy=memoryPolicy,
usb=usb,
) # display=display,
return api.system_service().vms_service().add(par).id return api.system_service().vms_service().add(par).id # type: ignore
finally: finally:
lock.release() lock.release()
@ -546,7 +620,7 @@ class Client:
api = self.__getApi() api = self.__getApi()
api.system_service().templates_service().service(templateId).remove() api.system_service().templates_service().service(templateId).remove() # type: ignore
# This returns nothing, if it fails it raises an exception # This returns nothing, if it fails it raises an exception
finally: finally:
lock.release() lock.release()
@ -573,12 +647,12 @@ class Client:
api = self.__getApi() api = self.__getApi()
try: try:
vm = api.system_service().vms_service().service(machineId).get() vm = api.system_service().vms_service().service(machineId).get() # type: ignore
if vm is None or vm.status is None: if vm is None or vm.status is None: # type: ignore
return 'unknown' return 'unknown'
return vm.status.value return vm.status.value # type: ignore
except Exception: # machine not found except Exception: # machine not found
return 'unknown' return 'unknown'
@ -601,7 +675,9 @@ class Client:
api = self.__getApi() api = self.__getApi()
vmService = api.system_service().vms_service().service(machineId) vmService: typing.Any = (
api.system_service().vms_service().service(machineId)
)
if vmService.get() is None: if vmService.get() is None:
raise Exception('Machine not found') raise Exception('Machine not found')
@ -625,7 +701,9 @@ class Client:
api = self.__getApi() api = self.__getApi()
vmService = api.system_service().vms_service().service(machineId) vmService: typing.Any = (
api.system_service().vms_service().service(machineId)
)
if vmService.get() is None: if vmService.get() is None:
raise Exception('Machine not found') raise Exception('Machine not found')
@ -649,7 +727,9 @@ class Client:
api = self.__getApi() api = self.__getApi()
vmService = api.system_service().vms_service().service(machineId) vmService: typing.Any = (
api.system_service().vms_service().service(machineId)
)
if vmService.get() is None: if vmService.get() is None:
raise Exception('Machine not found') raise Exception('Machine not found')
@ -673,7 +753,9 @@ class Client:
api = self.__getApi() api = self.__getApi()
vmService = api.system_service().vms_service().service(machineId) vmService: typing.Any = (
api.system_service().vms_service().service(machineId)
)
if vmService.get() is None: if vmService.get() is None:
raise Exception('Machine not found') raise Exception('Machine not found')
@ -692,12 +774,16 @@ class Client:
api = self.__getApi() api = self.__getApi()
vmService = api.system_service().vms_service().service(machineId) vmService: typing.Any = (
api.system_service().vms_service().service(machineId)
)
if vmService.get() is None: if vmService.get() is None:
raise Exception('Machine not found') raise Exception('Machine not found')
nic = vmService.nics_service().list()[0] # If has no nic, will raise an exception (IndexError) nic = vmService.nics_service().list()[
0
] # If has no nic, will raise an exception (IndexError)
nic.mac.address = macAddres nic.mac.address = macAddres
nicService = vmService.nics_service().service(nic.id) nicService = vmService.nics_service().service(nic.id)
nicService.update(nic) nicService.update(nic)
@ -715,13 +801,15 @@ class Client:
api = self.__getApi() api = self.__getApi()
usb = ovirt.types.Usb(enabled=True, type=ovirt.types.UsbType.NATIVE) usb = ovirt.types.Usb(enabled=True, type=ovirt.types.UsbType.NATIVE)
vms = api.system_service().vms_service().service(machineId) vms: typing.Any = api.system_service().vms_service().service(machineId)
vmu = ovirt.types.Vm(usb=usb) vmu = ovirt.types.Vm(usb=usb)
vms.update(vmu) vms.update(vmu)
finally: finally:
lock.release() lock.release()
def getConsoleConnection(self, machineId: str) -> typing.Optional[typing.MutableMapping[str, typing.Any]]: def getConsoleConnection(
self, machineId: str
) -> typing.Optional[typing.MutableMapping[str, typing.Any]]:
""" """
Gets the connetion info for the specified machine Gets the connetion info for the specified machine
""" """
@ -729,7 +817,9 @@ class Client:
lock.acquire(True) lock.acquire(True)
api = self.__getApi() api = self.__getApi()
vmService = api.system_service().vms_service().service(machineId) vmService: typing.Any = (
api.system_service().vms_service().service(machineId)
)
vm = vmService.get() vm = vmService.get()
if vm is None: if vm is None:
@ -743,8 +833,17 @@ class Client:
if display.certificate is not None: if display.certificate is not None:
cert_subject = display.certificate.subject cert_subject = display.certificate.subject
else: else:
for i in api.system_service().hosts_service().list(): for i in typing.cast(
for k in api.system_service().hosts_service().service(i.id).nics_service().list(): typing.Iterable, api.system_service().hosts_service().list()
):
for k in typing.cast(
typing.Iterable,
api.system_service()
.hosts_service()
.service(i.id)
.nics_service() # type: ignore
.list(),
):
if k.ip.address == display.address: if k.ip.address == display.address:
cert_subject = i.certificate.subject cert_subject = i.certificate.subject
break break
@ -759,10 +858,7 @@ class Client:
'secure_port': display.secure_port, 'secure_port': display.secure_port,
'monitors': display.monitors, 'monitors': display.monitors,
'cert_subject': cert_subject, 'cert_subject': cert_subject,
'ticket': { 'ticket': {'value': ticket.value, 'expiry': ticket.expiry},
'value': ticket.value,
'expiry': ticket.expiry
}
} }
except Exception: except Exception:

View File

@ -11,7 +11,7 @@
# this list of conditions and the following disclaimer. # this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice, # * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation # this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution. # and/or other materials provided with the distribution.u
# * Neither the name of Virtual Cable S.L. nor the names of its contributors # * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software # may be used to endorse or promote products derived from this software
# without specific prior written permission. # without specific prior written permission.
@ -49,7 +49,18 @@ if typing.TYPE_CHECKING:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
opCreate, opStart, opStop, opSuspend, opRemove, opWait, opError, opFinish, opRetry, opChangeMac = range(10) (
opCreate,
opStart,
opStop,
opSuspend,
opRemove,
opWait,
opError,
opFinish,
opRetry,
opChangeMac,
) = range(10)
NO_MORE_NAMES = 'NO-NAME-ERROR' NO_MORE_NAMES = 'NO-NAME-ERROR'
UP_STATES = ('up', 'reboot_in_progress', 'powering_up', 'restoring_state') UP_STATES = ('up', 'reboot_in_progress', 'powering_up', 'restoring_state')
@ -66,6 +77,7 @@ class OVirtLinkedDeployment(services.UserDeployment):
The logic for managing ovirt deployments (user machines in this case) is here. The logic for managing ovirt deployments (user machines in this case) is here.
""" """
# : Recheck every six seconds by default (for task methods) # : Recheck every six seconds by default (for task methods)
suggestedTime = 6 suggestedTime = 6
@ -100,15 +112,17 @@ class OVirtLinkedDeployment(services.UserDeployment):
""" """
Does nothing right here, we will use environment storage in this sample Does nothing right here, we will use environment storage in this sample
""" """
return b'\1'.join([ return b'\1'.join(
[
b'v1', b'v1',
self._name.encode('utf8'), self._name.encode('utf8'),
self._ip.encode('utf8'), self._ip.encode('utf8'),
self._mac.encode('utf8'), self._mac.encode('utf8'),
self._vmid.encode('utf8'), self._vmid.encode('utf8'),
self._reason.encode('utf8'), self._reason.encode('utf8'),
pickle.dumps(self._queue, protocol=0) pickle.dumps(self._queue, protocol=0),
]) ]
)
def unmarshal(self, data: bytes) -> None: def unmarshal(self, data: bytes) -> None:
""" """
@ -126,7 +140,9 @@ class OVirtLinkedDeployment(services.UserDeployment):
def getName(self) -> str: def getName(self) -> str:
if self._name == '': if self._name == '':
try: try:
self._name = self.nameGenerator().get(self.service().getBaseName(), self.service().getLenName()) self._name = self.nameGenerator().get(
self.service().getBaseName(), self.service().getLenName()
)
except KeyError: except KeyError:
return NO_MORE_NAMES return NO_MORE_NAMES
return self._name return self._name
@ -207,7 +223,9 @@ class OVirtLinkedDeployment(services.UserDeployment):
if self._vmid != '': if self._vmid != '':
self.service().stopMachine(self._vmid) self.service().stopMachine(self._vmid)
def getConsoleConnection(self) -> typing.Optional[typing.MutableMapping[str, typing.Any]]: def getConsoleConnection(
self,
) -> typing.Optional[typing.MutableMapping[str, typing.Any]]:
return self.service().getConsoleConnection(self._vmid) return self.service().getConsoleConnection(self._vmid)
def desktopLogin(self, username: str, password: str, domain: str = '') -> None: def desktopLogin(self, username: str, password: str, domain: str = '') -> None:
@ -215,7 +233,9 @@ class OVirtLinkedDeployment(services.UserDeployment):
if sys.platform == 'win32': if sys.platform == 'win32':
from uds import operations from uds import operations
operations.writeToPipe("\\\\.\\pipe\\VDSMDPipe", struct.pack('!IsIs', 1, '{username}'.encode('utf8'), 2, '{password}'.encode('utf8')), True) operations.writeToPipe("\\\\.\\pipe\\VDSMDPipe", struct.pack('!IsIs', 1, '{username}'.encode('utf8'), 2, '{password}'.encode('utf8')), True)
'''.format(username=username, password=password) '''.format(
username=username, password=password
)
# Post script to service # Post script to service
# operations.writeToPipe("\\\\.\\pipe\\VDSMDPipe", packet, True) # operations.writeToPipe("\\\\.\\pipe\\VDSMDPipe", packet, True)
dbService = self.dbservice() dbService = self.dbservice()
@ -254,8 +274,15 @@ if sys.platform == 'win32':
else: else:
self._queue = [opCreate, opChangeMac, opStart, opWait, opSuspend, opFinish] self._queue = [opCreate, opChangeMac, opStart, opWait, opSuspend, opFinish]
def __checkMachineState(self, chkState: typing.Union[typing.List[str], typing.Tuple[str, ...], str]) -> str: def __checkMachineState(
logger.debug('Checking that state of machine %s (%s) is %s', self._vmid, self._name, chkState) self, chkState: typing.Union[typing.List[str], typing.Tuple[str, ...], str]
) -> str:
logger.debug(
'Checking that state of machine %s (%s) is %s',
self._vmid,
self._name,
chkState,
)
state = self.service().getMachineState(self._vmid) state = self.service().getMachineState(self._vmid)
# If we want to check an state and machine does not exists (except in case that we whant to check this) # If we want to check an state and machine does not exists (except in case that we whant to check this)
@ -329,14 +356,16 @@ if sys.platform == 'win32':
opSuspend: self.__suspendMachine, opSuspend: self.__suspendMachine,
opWait: self.__wait, opWait: self.__wait,
opRemove: self.__remove, opRemove: self.__remove,
opChangeMac: self.__changeMac opChangeMac: self.__changeMac,
} }
try: try:
execFnc: typing.Optional[typing.Callable[[], str]] = fncs.get(op, None) execFnc: typing.Optional[typing.Callable[[], str]] = fncs.get(op, None)
if execFnc is None: if execFnc is None:
return self.__error('Unknown operation found at execution queue ({0})'.format(op)) return self.__error(
'Unknown operation found at execution queue ({0})'.format(op)
)
execFnc() execFnc()
@ -368,9 +397,13 @@ if sys.platform == 'win32':
templateId = self.publication().getTemplateId() templateId = self.publication().getTemplateId()
name = self.getName() name = self.getName()
if name == NO_MORE_NAMES: if name == NO_MORE_NAMES:
raise Exception('No more names available for this service. (Increase digits for this service to fix)') raise Exception(
'No more names available for this service. (Increase digits for this service to fix)'
)
name = self.service().sanitizeVmName(name) # oVirt don't let us to create machines with more than 15 chars!!! name = self.service().sanitizeVmName(
name
) # oVirt don't let us to create machines with more than 15 chars!!!
comments = 'UDS Linked clone' comments = 'UDS Linked clone'
self._vmid = self.service().deployFromTemplate(name, comments, templateId) self._vmid = self.service().deployFromTemplate(name, comments, templateId)
@ -409,7 +442,9 @@ if sys.platform == 'win32':
return State.RUNNING return State.RUNNING
if state != 'down' and state != 'suspended': if state != 'down' and state != 'suspended':
self.__pushFrontOp(opRetry) # Will call "check Retry", that will finish inmediatly and again call this one self.__pushFrontOp(
opRetry
) # Will call "check Retry", that will finish inmediatly and again call this one
self.service().startMachine(self._vmid) self.service().startMachine(self._vmid)
return State.RUNNING return State.RUNNING
@ -427,7 +462,9 @@ if sys.platform == 'win32':
return State.RUNNING return State.RUNNING
if state != 'up' and state != 'suspended': if state != 'up' and state != 'suspended':
self.__pushFrontOp(opRetry) # Will call "check Retry", that will finish inmediatly and again call this one self.__pushFrontOp(
opRetry
) # Will call "check Retry", that will finish inmediatly and again call this one
else: else:
self.service().stopMachine(self._vmid) self.service().stopMachine(self._vmid)
@ -446,7 +483,9 @@ if sys.platform == 'win32':
return State.RUNNING return State.RUNNING
if state != 'up': if state != 'up':
self.__pushFrontOp(opRetry) # Remember here, the return State.FINISH will make this retry be "poped" right ar return self.__pushFrontOp(
opRetry
) # Remember here, the return State.FINISH will make this retry be "poped" right ar return
else: else:
self.service().suspendMachine(self._vmid) self.service().suspendMachine(self._vmid)
@ -522,14 +561,18 @@ if sys.platform == 'win32':
opStop: self.__checkStop, opStop: self.__checkStop,
opSuspend: self.__checkSuspend, opSuspend: self.__checkSuspend,
opRemove: self.__checkRemoved, opRemove: self.__checkRemoved,
opChangeMac: self.__checkMac opChangeMac: self.__checkMac,
} }
try: try:
chkFnc: typing.Optional[typing.Optional[typing.Callable[[], str]]] = fncs.get(op, None) chkFnc: typing.Optional[
typing.Optional[typing.Callable[[], str]]
] = fncs.get(op, None)
if chkFnc is None: if chkFnc is None:
return self.__error('Unknown operation found at check queue ({0})'.format(op)) return self.__error(
'Unknown operation found at check queue ({0})'.format(op)
)
state = chkFnc() state = chkFnc()
if state == State.FINISHED: if state == State.FINISHED:
@ -613,8 +656,16 @@ if sys.platform == 'win32':
opError: 'error', opError: 'error',
opFinish: 'finish', opFinish: 'finish',
opRetry: 'retry', opRetry: 'retry',
opChangeMac: 'changing mac' opChangeMac: 'changing mac',
}.get(op, '????') }.get(op, '????')
def __debug(self, txt): def __debug(self, txt):
logger.debug('State at %s: name: %s, ip: %s, mac: %s, vmid:%s, queue: %s', txt, self._name, self._ip, self._mac, self._vmid, [OVirtLinkedDeployment.__op2str(op) for op in self._queue]) logger.debug(
'State at %s: name: %s, ip: %s, mac: %s, vmid:%s, queue: %s',
txt,
self._name,
self._ip,
self._mac,
self._vmid,
[OVirtLinkedDeployment.__op2str(op) for op in self._queue],
)

View File

@ -13,12 +13,14 @@ if typing.TYPE_CHECKING:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def getResources(parameters: typing.Any) -> typing.List[typing.Dict[str, typing.Any]]: def getResources(parameters: typing.Any) -> typing.List[typing.Dict[str, typing.Any]]:
""" """
This helper is designed as a callback for machine selector, so we can provide valid clusters and datastores domains based on it This helper is designed as a callback for machine selector, so we can provide valid clusters and datastores domains based on it
""" """
from .provider import OVirtProvider from .provider import OVirtProvider
from uds.core.environment import Environment from uds.core.environment import Environment
logger.debug('Parameters received by getResources Helper: %s', parameters) logger.debug('Parameters received by getResources Helper: %s', parameters)
env = Environment(parameters['ev']) env = Environment(parameters['ev'])
provider: 'OVirtProvider' = OVirtProvider(env) provider: 'OVirtProvider' = OVirtProvider(env)
@ -31,15 +33,23 @@ def getResources(parameters: typing.Any) -> typing.List[typing.Dict[str, typing.
# Get storages for that datacenter # Get storages for that datacenter
for storage in provider.getDatacenterInfo(ci['datacenter_id'])['storage']: for storage in provider.getDatacenterInfo(ci['datacenter_id'])['storage']:
if storage['type'] == 'data': if storage['type'] == 'data':
space, free = (storage['available'] + storage['used']) / 1024 / 1024 / 1024, storage['available'] / 1024 / 1024 / 1024 space, free = (
storage['available'] + storage['used']
) / 1024 / 1024 / 1024, storage['available'] / 1024 / 1024 / 1024
res.append({'id': storage['id'], 'text': "%s (%4.2f GB/%4.2f GB) %s" % (storage['name'], space, free, storage['active'] and '(ok)' or '(disabled)')}) res.append(
data = [
{ {
'name': 'datastore', 'id': storage['id'],
'values': res 'text': "%s (%4.2f GB/%4.2f GB) %s"
% (
storage['name'],
space,
free,
storage['active'] and '(ok)' or '(disabled)',
),
} }
] )
data = [{'name': 'datastore', 'values': res}]
logger.debug('return data: %s', data) logger.debug('return data: %s', data)
return data return data

View File

@ -58,7 +58,9 @@ class OVirtDeferredRemoval(jobs.Job):
@staticmethod @staticmethod
def remove(providerInstance: 'OVirtProvider', vmId: str) -> None: def remove(providerInstance: 'OVirtProvider', vmId: str) -> None:
logger.debug('Adding %s from %s to defeffed removal process', vmId, providerInstance) logger.debug(
'Adding %s from %s to defeffed removal process', vmId, providerInstance
)
OVirtDeferredRemoval.counter += 1 OVirtDeferredRemoval.counter += 1
try: try:
# Tries to stop machine sync when found, if any error is done, defer removal for a scheduled task # Tries to stop machine sync when found, if any error is done, defer removal for a scheduled task
@ -72,7 +74,11 @@ class OVirtDeferredRemoval(jobs.Job):
except Exception as e: except Exception as e:
providerInstance.storage.saveData('tr' + vmId, vmId, attr1='tRm') providerInstance.storage.saveData('tr' + vmId, vmId, attr1='tRm')
logger.info('Machine %s could not be removed right now, queued for later: %s', vmId, e) logger.info(
'Machine %s could not be removed right now, queued for later: %s',
vmId,
e,
)
except Exception as e: except Exception as e:
logger.warning('Exception got queuing for Removal: %s', e) logger.warning('Exception got queuing for Removal: %s', e)
@ -84,7 +90,9 @@ class OVirtDeferredRemoval(jobs.Job):
provider: Provider provider: Provider
# Look for Providers of type Ovirt # Look for Providers of type Ovirt
for provider in Provider.objects.filter(maintenance_mode=False, data_type=OVirtProvider.typeType): for provider in Provider.objects.filter(
maintenance_mode=False, data_type=OVirtProvider.typeType
):
logger.debug('Provider %s if os type ovirt', provider) logger.debug('Provider %s if os type ovirt', provider)
storage = provider.getEnvironment().storage storage = provider.getEnvironment().storage

View File

@ -52,7 +52,9 @@ logger = logging.getLogger(__name__)
CACHE_TIME_FOR_SERVER = 1800 CACHE_TIME_FOR_SERVER = 1800
class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-public-methods class OVirtProvider(
services.ServiceProvider
): # pylint: disable=too-many-public-methods
""" """
This class represents the sample services provider This class represents the sample services provider
@ -69,6 +71,7 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi
we MUST register it at package __init__. we MUST register it at package __init__.
""" """
# : What kind of services we offer, this are classes inherited from Service # : What kind of services we offer, this are classes inherited from Service
offers = [OVirtLinkedService] offers = [OVirtLinkedService]
# : Name to show the administrator. This string will be translated BEFORE # : Name to show the administrator. This string will be translated BEFORE
@ -103,19 +106,74 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi
values=[ values=[
gui.choiceItem('4', '4.x'), gui.choiceItem('4', '4.x'),
], ],
defvalue='4' # Default value is the ID of the choicefield defvalue='4', # Default value is the ID of the choicefield
) )
host = gui.TextField(length=64, label=_('Host'), order=2, tooltip=_('oVirt Server IP or Hostname'), required=True) host = gui.TextField(
username = gui.TextField(length=32, label=_('Username'), order=3, tooltip=_('User with valid privileges on oVirt, (use "user@domain" form)'), required=True, defvalue='admin@internal') length=64,
password = gui.PasswordField(lenth=32, label=_('Password'), order=4, tooltip=_('Password of the user of oVirt'), required=True) label=_('Host'),
order=2,
tooltip=_('oVirt Server IP or Hostname'),
required=True,
)
username = gui.TextField(
length=32,
label=_('Username'),
order=3,
tooltip=_('User with valid privileges on oVirt, (use "user@domain" form)'),
required=True,
defvalue='admin@internal',
)
password = gui.PasswordField(
lenth=32,
label=_('Password'),
order=4,
tooltip=_('Password of the user of oVirt'),
required=True,
)
maxPreparingServices = gui.NumericField(length=3, label=_('Creation concurrency'), defvalue='10', minValue=1, maxValue=65536, order=50, tooltip=_('Maximum number of concurrently creating VMs'), required=True, tab=gui.ADVANCED_TAB) maxPreparingServices = gui.NumericField(
maxRemovingServices = gui.NumericField(length=3, label=_('Removal concurrency'), defvalue='5', minValue=1, maxValue=65536, order=51, tooltip=_('Maximum number of concurrently removing VMs'), required=True, tab=gui.ADVANCED_TAB) length=3,
label=_('Creation concurrency'),
defvalue='10',
minValue=1,
maxValue=65536,
order=50,
tooltip=_('Maximum number of concurrently creating VMs'),
required=True,
tab=gui.ADVANCED_TAB,
)
maxRemovingServices = gui.NumericField(
length=3,
label=_('Removal concurrency'),
defvalue='5',
minValue=1,
maxValue=65536,
order=51,
tooltip=_('Maximum number of concurrently removing VMs'),
required=True,
tab=gui.ADVANCED_TAB,
)
timeout = gui.NumericField(length=3, label=_('Timeout'), defvalue='10', order=90, tooltip=_('Timeout in seconds of connection to oVirt'), required=True, tab=gui.ADVANCED_TAB) timeout = gui.NumericField(
macsRange = gui.TextField(length=36, label=_('Macs range'), defvalue='52:54:00:00:00:00-52:54:00:FF:FF:FF', order=91, rdonly=True, length=3,
tooltip=_('Range of valid macs for UDS managed machines'), required=True, tab=gui.ADVANCED_TAB) label=_('Timeout'),
defvalue='10',
order=90,
tooltip=_('Timeout in seconds of connection to oVirt'),
required=True,
tab=gui.ADVANCED_TAB,
)
macsRange = gui.TextField(
length=36,
label=_('Macs range'),
defvalue='52:54:00:00:00:00-52:54:00:FF:FF:FF',
order=91,
rdonly=True,
tooltip=_('Range of valid macs for UDS managed machines'),
required=True,
tab=gui.ADVANCED_TAB,
)
# Own variables # Own variables
_api: typing.Optional[client.Client] = None _api: typing.Optional[client.Client] = None
@ -129,7 +187,13 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi
Returns the connection API object for oVirt (using ovirtsdk) Returns the connection API object for oVirt (using ovirtsdk)
""" """
if self._api is None: if self._api is None:
self._api = client.Client(self.host.value, self.username.value, self.password.value, self.timeout.value, self.cache) self._api = client.Client(
self.host.value,
self.username.value,
self.password.value,
self.timeout.value,
self.cache,
)
return self._api return self._api
@ -164,7 +228,9 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi
""" """
return list(self.__getApi().isFullyFunctionalVersion()) return list(self.__getApi().isFullyFunctionalVersion())
def getMachines(self, force: bool = False) -> typing.List[typing.MutableMapping[str, typing.Any]]: def getMachines(
self, force: bool = False
) -> typing.List[typing.MutableMapping[str, typing.Any]]:
""" """
Obtains the list of machines inside oVirt. Obtains the list of machines inside oVirt.
Machines starting with UDS are filtered out Machines starting with UDS are filtered out
@ -182,7 +248,9 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi
return self.__getApi().getVms(force) return self.__getApi().getVms(force)
def getClusters(self, force: bool = False) -> typing.List[typing.MutableMapping[str, typing.Any]]: def getClusters(
self, force: bool = False
) -> typing.List[typing.MutableMapping[str, typing.Any]]:
""" """
Obtains the list of clusters inside oVirt. Obtains the list of clusters inside oVirt.
@ -200,7 +268,9 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi
return self.__getApi().getClusters(force) return self.__getApi().getClusters(force)
def getClusterInfo(self, clusterId: str, force: bool = False) -> typing.MutableMapping[str, typing.Any]: def getClusterInfo(
self, clusterId: str, force: bool = False
) -> typing.MutableMapping[str, typing.Any]:
""" """
Obtains the cluster info Obtains the cluster info
@ -218,7 +288,9 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi
""" """
return self.__getApi().getClusterInfo(clusterId, force) return self.__getApi().getClusterInfo(clusterId, force)
def getDatacenterInfo(self, datacenterId: str, force: bool = False) -> typing.MutableMapping[str, typing.Any]: def getDatacenterInfo(
self, datacenterId: str, force: bool = False
) -> typing.MutableMapping[str, typing.Any]:
""" """
Obtains the datacenter info Obtains the datacenter info
@ -246,7 +318,9 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi
""" """
return self.__getApi().getDatacenterInfo(datacenterId, force) return self.__getApi().getDatacenterInfo(datacenterId, force)
def getStorageInfo(self, storageId: str, force: bool = False) -> typing.MutableMapping[str, typing.Any]: def getStorageInfo(
self, storageId: str, force: bool = False
) -> typing.MutableMapping[str, typing.Any]:
""" """
Obtains the storage info Obtains the storage info
@ -275,7 +349,7 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi
machineId: str, machineId: str,
clusterId: str, clusterId: str,
storageId: str, storageId: str,
displayType: str displayType: str,
) -> str: ) -> str:
""" """
Publish the machine (makes a template from it so we can create COWs) and returns the template id of Publish the machine (makes a template from it so we can create COWs) and returns the template id of
@ -291,7 +365,9 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi
Returns Returns
Raises an exception if operation could not be acomplished, or returns the id of the template being created. Raises an exception if operation could not be acomplished, or returns the id of the template being created.
""" """
return self.__getApi().makeTemplate(name, comments, machineId, clusterId, storageId, displayType) return self.__getApi().makeTemplate(
name, comments, machineId, clusterId, storageId, displayType
)
def getTemplateState(self, templateId: str) -> str: def getTemplateState(self, templateId: str) -> str:
""" """
@ -341,7 +417,7 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi
displayType: str, displayType: str,
usbType: str, usbType: str,
memoryMB: int, memoryMB: int,
guaranteedMB: int guaranteedMB: int,
) -> str: ) -> str:
""" """
Deploys a virtual machine on selected cluster from selected template Deploys a virtual machine on selected cluster from selected template
@ -358,7 +434,16 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi
Returns: Returns:
Id of the machine being created form template Id of the machine being created form template
""" """
return self.__getApi().deployFromTemplate(name, comments, templateId, clusterId, displayType, usbType, memoryMB, guaranteedMB) return self.__getApi().deployFromTemplate(
name,
comments,
templateId,
clusterId,
displayType,
usbType,
memoryMB,
guaranteedMB,
)
def startMachine(self, machineId: str) -> None: def startMachine(self, machineId: str) -> None:
""" """
@ -418,7 +503,9 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi
def getMacRange(self) -> str: def getMacRange(self) -> str:
return self.macsRange.value return self.macsRange.value
def getConsoleConnection(self, machineId: str) -> typing.Optional[typing.MutableMapping[str, typing.Any]]: def getConsoleConnection(
self, machineId: str
) -> typing.Optional[typing.MutableMapping[str, typing.Any]]:
return self.__getApi().getConsoleConnection(machineId) return self.__getApi().getConsoleConnection(machineId)
@staticmethod @staticmethod

View File

@ -50,7 +50,9 @@ class OVirtPublication(Publication):
This class provides the publication of a oVirtLinkedService This class provides the publication of a oVirtLinkedService
""" """
suggestedTime = 20 # : Suggested recheck time if publication is unfinished in seconds suggestedTime = (
20 # : Suggested recheck time if publication is unfinished in seconds
)
_name: str _name: str
_reason: str _reason: str
_destroyAfter: str _destroyAfter: str
@ -80,7 +82,16 @@ class OVirtPublication(Publication):
""" """
returns data from an instance of Sample Publication serialized returns data from an instance of Sample Publication serialized
""" """
return '\t'.join(['v1', self._name, self._reason, self._destroyAfter, self._templateId, self._state]).encode('utf8') return '\t'.join(
[
'v1',
self._name,
self._reason,
self._destroyAfter,
self._templateId,
self._state,
]
).encode('utf8')
def unmarshal(self, data: bytes) -> None: def unmarshal(self, data: bytes) -> None:
""" """
@ -89,14 +100,24 @@ class OVirtPublication(Publication):
logger.debug('Data: %s', data) logger.debug('Data: %s', data)
vals = data.decode('utf8').split('\t') vals = data.decode('utf8').split('\t')
if vals[0] == 'v1': if vals[0] == 'v1':
self._name, self._reason, self._destroyAfter, self._templateId, self._state = vals[1:] (
self._name,
self._reason,
self._destroyAfter,
self._templateId,
self._state,
) = vals[1:]
def publish(self) -> str: def publish(self) -> str:
""" """
Realizes the publication of the service Realizes the publication of the service
""" """
self._name = self.service().sanitizeVmName('UDSP ' + self.dsName() + "-" + str(self.revision())) self._name = self.service().sanitizeVmName(
comments = _('UDS pub for {0} at {1}').format(self.dsName(), str(datetime.now()).split('.')[0]) 'UDSP ' + self.dsName() + "-" + str(self.revision())
)
comments = _('UDS pub for {0} at {1}').format(
self.dsName(), str(datetime.now()).split('.')[0]
)
self._reason = '' # No error, no reason for it self._reason = '' # No error, no reason for it
self._destroyAfter = 'f' self._destroyAfter = 'f'
self._state = 'locked' self._state = 'locked'

View File

@ -57,6 +57,7 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods
""" """
oVirt Linked clones service. This is based on creating a template from selected vm, and then use it to oVirt Linked clones service. This is based on creating a template from selected vm, and then use it to
""" """
# : Name to show the administrator. This string will be translated BEFORE # : Name to show the administrator. This string will be translated BEFORE
# : sending it to administration interface, so don't forget to # : sending it to administration interface, so don't forget to
# : mark it as _ (using ugettext_noop) # : mark it as _ (using ugettext_noop)
@ -113,9 +114,10 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods
fills={ fills={
'callbackName': 'ovFillResourcesFromCluster', 'callbackName': 'ovFillResourcesFromCluster',
'function': helpers.getResources, 'function': helpers.getResources,
'parameters': ['cluster', 'ov', 'ev'] 'parameters': ['cluster', 'ov', 'ev'],
}, },
tooltip=_("Cluster to contain services"), required=True tooltip=_("Cluster to contain services"),
required=True,
) )
datastore = gui.ChoiceField( datastore = gui.ChoiceField(
@ -123,7 +125,7 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods
rdonly=False, rdonly=False,
order=101, order=101,
tooltip=_('Datastore domain where to publish and put incrementals'), tooltip=_('Datastore domain where to publish and put incrementals'),
required=True required=True,
) )
minSpaceGB = gui.NumericField( minSpaceGB = gui.NumericField(
@ -133,7 +135,7 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods
minValue=0, minValue=0,
order=102, order=102,
tooltip=_('Minimal free space in GB'), tooltip=_('Minimal free space in GB'),
required=True required=True,
) )
machine = gui.ChoiceField( machine = gui.ChoiceField(
@ -141,7 +143,7 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods
order=110, order=110,
tooltip=_('Service base machine'), tooltip=_('Service base machine'),
tab=_('Machine'), tab=_('Machine'),
required=True required=True,
) )
memory = gui.NumericField( memory = gui.NumericField(
@ -153,7 +155,7 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods
order=111, order=111,
tooltip=_('Memory assigned to machines'), tooltip=_('Memory assigned to machines'),
tab=_('Machine'), tab=_('Machine'),
required=True required=True,
) )
memoryGuaranteed = gui.NumericField( memoryGuaranteed = gui.NumericField(
@ -165,7 +167,7 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods
order=112, order=112,
tooltip=_('Physical memory guaranteed to machines'), tooltip=_('Physical memory guaranteed to machines'),
tab=_('Machine'), tab=_('Machine'),
required=True required=True,
) )
usb = gui.ChoiceField( usb = gui.ChoiceField(
@ -179,7 +181,7 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods
# gui.choiceItem('legacy', 'legacy (deprecated)'), # gui.choiceItem('legacy', 'legacy (deprecated)'),
], ],
tab=_('Machine'), tab=_('Machine'),
defvalue='1' # Default value is the ID of the choicefield defvalue='1', # Default value is the ID of the choicefield
) )
display = gui.ChoiceField( display = gui.ChoiceField(
@ -187,12 +189,9 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods
rdonly=False, rdonly=False,
order=114, order=114,
tooltip=_('Display type (only for administration purposes)'), tooltip=_('Display type (only for administration purposes)'),
values=[ values=[gui.choiceItem('spice', 'Spice'), gui.choiceItem('vnc', 'Vnc')],
gui.choiceItem('spice', 'Spice'),
gui.choiceItem('vnc', 'Vnc')
],
tab=_('Machine'), tab=_('Machine'),
defvalue='1' # Default value is the ID of the choicefield defvalue='1', # Default value is the ID of the choicefield
) )
baseName = gui.TextField( baseName = gui.TextField(
label=_('Machine Names'), label=_('Machine Names'),
@ -200,7 +199,7 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods
order=115, order=115,
tooltip=_('Base name for clones from this machine'), tooltip=_('Base name for clones from this machine'),
tab=_('Machine'), tab=_('Machine'),
required=True required=True,
) )
lenName = gui.NumericField( lenName = gui.NumericField(
@ -210,11 +209,13 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods
order=116, order=116,
tooltip=_('Size of numeric part for the names of these machines'), tooltip=_('Size of numeric part for the names of these machines'),
tab=_('Machine'), tab=_('Machine'),
required=True required=True,
) )
ov = gui.HiddenField(value=None) ov = gui.HiddenField(value=None)
ev = gui.HiddenField(value=None) # We need to keep the env so we can instantiate the Provider ev = gui.HiddenField(
value=None
) # We need to keep the env so we can instantiate the Provider
def initialize(self, values: 'Module.ValuesType') -> None: def initialize(self, values: 'Module.ValuesType') -> None:
""" """
@ -226,7 +227,9 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods
if values: if values:
tools.checkValidBasename(self.baseName.value, self.lenName.num()) tools.checkValidBasename(self.baseName.value, self.lenName.num())
if int(self.memory.value) < 256 or int(self.memoryGuaranteed.value) < 256: if int(self.memory.value) < 256 or int(self.memoryGuaranteed.value) < 256:
raise Service.ValidationException(_('The minimum allowed memory is 256 Mb')) raise Service.ValidationException(
_('The minimum allowed memory is 256 Mb')
)
if int(self.memoryGuaranteed.value) > int(self.memory.value): if int(self.memoryGuaranteed.value) > int(self.memory.value):
self.memoryGuaranteed.value = self.memory.value self.memoryGuaranteed.value = self.memory.value
@ -275,7 +278,11 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods
logger.debug('Datastore Info: %s', info) logger.debug('Datastore Info: %s', info)
availableGB = info['available'] / (1024 * 1024 * 1024) availableGB = info['available'] / (1024 * 1024 * 1024)
if availableGB < self.minSpaceGB.num(): if availableGB < self.minSpaceGB.num():
raise Exception('Not enough free space available: (Needs at least {0} GB and there is only {1} GB '.format(self.minSpaceGB.num(), availableGB)) raise Exception(
'Not enough free space available: (Needs at least {0} GB and there is only {1} GB '.format(
self.minSpaceGB.num(), availableGB
)
)
def sanitizeVmName(self, name: str) -> str: def sanitizeVmName(self, name: str) -> str:
""" """
@ -301,7 +308,14 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods
# Get storages for that datacenter # Get storages for that datacenter
self.datastoreHasSpace() self.datastoreHasSpace()
return self.parent().makeTemplate(name, comments, self.machine.value, self.cluster.value, self.datastore.value, self.display.value) return self.parent().makeTemplate(
name,
comments,
self.machine.value,
self.cluster.value,
self.datastore.value,
self.display.value,
)
def getTemplateState(self, templateId: str) -> str: def getTemplateState(self, templateId: str) -> str:
""" """
@ -333,8 +347,16 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods
""" """
logger.debug('Deploying from template %s machine %s', templateId, name) logger.debug('Deploying from template %s machine %s', templateId, name)
self.datastoreHasSpace() self.datastoreHasSpace()
return self.parent().deployFromTemplate(name, comments, templateId, self.cluster.value, return self.parent().deployFromTemplate(
self.display.value, self.usb.value, int(self.memory.value), int(self.memoryGuaranteed.value)) name,
comments,
templateId,
self.cluster.value,
self.display.value,
self.usb.value,
int(self.memory.value),
int(self.memoryGuaranteed.value),
)
def removeTemplate(self, templateId: str) -> None: def removeTemplate(self, templateId: str) -> None:
""" """
@ -440,5 +462,7 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods
""" """
return self.display.value return self.display.value
def getConsoleConnection(self, machineId: str) -> typing.Optional[typing.MutableMapping[str, typing.Any]]: def getConsoleConnection(
self, machineId: str
) -> typing.Optional[typing.MutableMapping[str, typing.Any]]:
return self.parent().getConsoleConnection(machineId) return self.parent().getConsoleConnection(machineId)

View File

@ -48,6 +48,7 @@ if typing.TYPE_CHECKING:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class OpenGnsysMaintainer(jobs.Job): class OpenGnsysMaintainer(jobs.Job):
frecuency = 60 * 60 * 4 # Once every 4 hours frecuency = 60 * 60 * 4 # Once every 4 hours
friendly_name = 'OpenGnsys cache renewal job' friendly_name = 'OpenGnsys cache renewal job'
@ -57,18 +58,29 @@ class OpenGnsysMaintainer(jobs.Job):
# Look for Providers of type VMWareVCServiceProvider # Look for Providers of type VMWareVCServiceProvider
provider: models.Provider provider: models.Provider
for provider in models.Provider.objects.filter(maintenance_mode=False, data_type=OGProvider.typeType): for provider in models.Provider.objects.filter(
maintenance_mode=False, data_type=OGProvider.typeType
):
logger.debug('Provider %s is type openGnsys', provider) logger.debug('Provider %s is type openGnsys', provider)
# Locate all services inside the provider # Locate all services inside the provider
service: models.Service service: models.Service
for service in provider.services.all(): for service in provider.services.all():
instance: OGService = typing.cast(OGService, service.getInstance()) instance: OGService = typing.cast(OGService, service.getInstance())
since = models.getSqlDatetime() - datetime.timedelta(hours=instance.maxReservationTime.num()-8) # If less than 8 hours of reservation... since = models.getSqlDatetime() - datetime.timedelta(
hours=instance.maxReservationTime.num() - 8
) # If less than 8 hours of reservation...
# Now mark for removal every CACHED service that is about to expire its reservation on OpenGnsys # Now mark for removal every CACHED service that is about to expire its reservation on OpenGnsys
userService: models.UserService userService: models.UserService
for userService in models.UserService.objects.filter(deployed_service__service=service, creation_date__lt=since, cache_level=1): for userService in models.UserService.objects.filter(
logger.info('The cached user service %s is about to expire. Removing it so it can be recreated', userService) deployed_service__service=service,
creation_date__lt=since,
cache_level=1,
):
logger.info(
'The cached user service %s is about to expire. Removing it so it can be recreated',
userService,
)
userService.remove() userService.remove()
logger.debug('OpenGnsys job finished') logger.debug('OpenGnsys job finished')

View File

@ -140,7 +140,9 @@ class OGService(Service):
label=_('Start if unavailable'), label=_('Start if unavailable'),
defvalue=gui.TRUE, defvalue=gui.TRUE,
order=111, order=111,
tooltip=_('If active, machines that are not available on user connect (on some OS) will try to power on through OpenGnsys.'), tooltip=_(
'If active, machines that are not available on user connect (on some OS) will try to power on through OpenGnsys.'
),
) )
ov = gui.HiddenField(value=None) ov = gui.HiddenField(value=None)
@ -180,7 +182,7 @@ class OGService(Service):
machineId, machineId,
self.getLoginNotifyURL(uuid, token), self.getLoginNotifyURL(uuid, token),
self.getLogoutNotifyURL(uuid, token), self.getLogoutNotifyURL(uuid, token),
self.getReleaseURL(uuid, token) self.getReleaseURL(uuid, token),
) )
def notifyDeadline( def notifyDeadline(
@ -197,7 +199,7 @@ class OGService(Service):
accessURL=self.parent().getUDSServerAccessUrl(), accessURL=self.parent().getUDSServerAccessUrl(),
uuid=uuid, uuid=uuid,
token=token, token=token,
message=message message=message,
) )
def getLoginNotifyURL(self, uuid: str, token: str) -> str: def getLoginNotifyURL(self, uuid: str, token: str) -> str:

View File

@ -53,6 +53,7 @@ opCreate, opStart, opShutdown, opRemove, opWait, opError, opFinish, opRetry = ra
NO_MORE_NAMES = 'NO-NAME-ERROR' NO_MORE_NAMES = 'NO-NAME-ERROR'
class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods
# : Recheck every six seconds by default (for task methods) # : Recheck every six seconds by default (for task methods)
suggestedTime = 6 suggestedTime = 6
@ -65,7 +66,6 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods
_reason: str = '' _reason: str = ''
_queue: typing.List[int] _queue: typing.List[int]
def initialize(self): def initialize(self):
self._name = '' self._name = ''
self._ip = '' self._ip = ''
@ -85,15 +85,17 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods
# Serializable needed methods # Serializable needed methods
def marshal(self) -> bytes: def marshal(self) -> bytes:
return b'\1'.join([ return b'\1'.join(
[
b'v1', b'v1',
self._name.encode('utf8'), self._name.encode('utf8'),
self._ip.encode('utf8'), self._ip.encode('utf8'),
self._mac.encode('utf8'), self._mac.encode('utf8'),
self._vmid.encode('utf8'), self._vmid.encode('utf8'),
self._reason.encode('utf8'), self._reason.encode('utf8'),
pickle.dumps(self._queue, protocol=0) pickle.dumps(self._queue, protocol=0),
]) ]
)
def unmarshal(self, data: bytes) -> None: def unmarshal(self, data: bytes) -> None:
vals = data.split(b'\1') vals = data.split(b'\1')
@ -108,7 +110,9 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods
def getName(self) -> str: def getName(self) -> str:
if self._name == '': if self._name == '':
try: try:
self._name = self.nameGenerator().get(self.service().getBaseName(), self.service().getLenName()) self._name = self.nameGenerator().get(
self.service().getBaseName(), self.service().getLenName()
)
except KeyError: except KeyError:
return NO_MORE_NAMES return NO_MORE_NAMES
return self._name return self._name
@ -179,11 +183,19 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods
self._queue = [opCreate, opStart, opWait, opShutdown, opFinish] self._queue = [opCreate, opStart, opWait, opShutdown, opFinish]
def __checkMachineState(self, chkState: on.types.VmState) -> str: def __checkMachineState(self, chkState: on.types.VmState) -> str:
logger.debug('Checking that state of machine %s (%s) is %s', self._vmid, self._name, chkState) logger.debug(
'Checking that state of machine %s (%s) is %s',
self._vmid,
self._name,
chkState,
)
state = self.service().getMachineState(self._vmid) state = self.service().getMachineState(self._vmid)
# If we want to check an state and machine does not exists (except in case that we whant to check this) # If we want to check an state and machine does not exists (except in case that we whant to check this)
if state in [on.types.VmState.UNKNOWN, on.types.VmState.DONE]: # @UndefinedVariable if state in [
on.types.VmState.UNKNOWN,
on.types.VmState.DONE,
]: # @UndefinedVariable
return self.__error('Machine not found') return self.__error('Machine not found')
ret = State.RUNNING ret = State.RUNNING
@ -260,7 +272,9 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods
execFnc: typing.Optional[typing.Callable[[], str]] = fncs.get(op, None) execFnc: typing.Optional[typing.Callable[[], str]] = fncs.get(op, None)
if execFnc is None: if execFnc is None:
return self.__error('Unknown operation found at execution queue ({0})'.format(op)) return self.__error(
'Unknown operation found at execution queue ({0})'.format(op)
)
execFnc() execFnc()
@ -293,9 +307,13 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods
templateId = self.publication().getTemplateId() templateId = self.publication().getTemplateId()
name = self.getName() name = self.getName()
if name == NO_MORE_NAMES: if name == NO_MORE_NAMES:
raise Exception('No more names available for this service. (Increase digits for this service to fix)') raise Exception(
'No more names available for this service. (Increase digits for this service to fix)'
)
name = self.service().sanitizeVmName(name) # OpenNebula don't let us to create machines with more than 15 chars!!! name = self.service().sanitizeVmName(
name
) # OpenNebula don't let us to create machines with more than 15 chars!!!
self._vmid = self.service().deployFromTemplate(name, templateId) self._vmid = self.service().deployFromTemplate(name, templateId)
if self._vmid is None: if self._vmid is None:
@ -395,7 +413,9 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods
chkFnc: typing.Optional[typing.Callable[[], str]] = fncs.get(op, None) chkFnc: typing.Optional[typing.Callable[[], str]] = fncs.get(op, None)
if chkFnc is None: if chkFnc is None:
return self.__error('Unknown operation found at check queue ({0})'.format(op)) return self.__error(
'Unknown operation found at check queue ({0})'.format(op)
)
state = chkFnc() state = chkFnc()
if state == State.FINISHED: if state == State.FINISHED:
@ -476,4 +496,12 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods
}.get(op, '????') }.get(op, '????')
def __debug(self, txt: str) -> None: def __debug(self, txt: str) -> None:
logger.debug('State at %s: name: %s, ip: %s, mac: %s, vmid:%s, queue: %s', txt, self._name, self._ip, self._mac, self._vmid, [LiveDeployment.__op2str(op) for op in self._queue]) logger.debug(
'State at %s: name: %s, ip: %s, mac: %s, vmid:%s, queue: %s',
txt,
self._name,
self._ip,
self._mac,
self._vmid,
[LiveDeployment.__op2str(op) for op in self._queue],
)

View File

@ -39,7 +39,10 @@ if typing.TYPE_CHECKING:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def enumerateDatastores(api: 'client.OpenNebulaClient', datastoreType: int = 0) -> typing.Iterable['types.StorageType']:
def enumerateDatastores(
api: 'client.OpenNebulaClient', datastoreType: int = 0
) -> typing.Iterable['types.StorageType']:
""" """
0 seems to be images datastore 0 seems to be images datastore
""" """

View File

@ -45,13 +45,18 @@ if typing.TYPE_CHECKING:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def getTemplates(api: 'client.OpenNebulaClient', force: bool = False) -> typing.Iterable[types.TemplateType]:
def getTemplates(
api: 'client.OpenNebulaClient', force: bool = False
) -> typing.Iterable[types.TemplateType]:
for t in api.enumTemplates(): for t in api.enumTemplates():
if t.name[:4] != 'UDSP': if t.name[:4] != 'UDSP':
yield t yield t
def create(api: 'client.OpenNebulaClient', fromTemplateId: str, name: str, toDataStore: str) -> str: def create(
api: 'client.OpenNebulaClient', fromTemplateId: str, name: str, toDataStore: str
) -> str:
""" """
Publish the machine (makes a template from it so we can create COWs) and returns the template id of Publish the machine (makes a template from it so we can create COWs) and returns the template id of
the creating machine the creating machine
@ -92,7 +97,11 @@ def create(api: 'client.OpenNebulaClient', fromTemplateId: str, name: str, toDat
try: try:
imgId = imgs[imgName.strip()] imgId = imgs[imgName.strip()]
except KeyError: except KeyError:
raise Exception('Image "{}" could not be found!. Check the opennebula template'.format(imgName.strip())) raise Exception(
'Image "{}" could not be found!. Check the opennebula template'.format(
imgName.strip()
)
)
else: else:
fromId = True fromId = True
node = imgIds[0].childNodes[0] node = imgIds[0].childNodes[0]
@ -105,7 +114,9 @@ def create(api: 'client.OpenNebulaClient', fromTemplateId: str, name: str, toDat
# Now clone the image # Now clone the image
imgName = sanitizeName(name + ' DSK ' + str(counter)) imgName = sanitizeName(name + ' DSK ' + str(counter))
newId = api.cloneImage(imgId, imgName, toDataStore) # api.call('image.clone', int(imgId), imgName, int(toDataStore)) newId = api.cloneImage(
imgId, imgName, toDataStore
) # api.call('image.clone', int(imgId), imgName, int(toDataStore))
# Now Store id/name # Now Store id/name
if fromId is True: if fromId is True:
node.data = str(newId) node.data = str(newId)
@ -120,7 +131,9 @@ def create(api: 'client.OpenNebulaClient', fromTemplateId: str, name: str, toDat
except Exception as e: except Exception as e:
logger.exception('Creating template on OpenNebula') logger.exception('Creating template on OpenNebula')
try: try:
api.deleteTemplate(templateId) # Try to remove created template in case of fail api.deleteTemplate(
templateId
) # Try to remove created template in case of fail
except Exception: except Exception:
pass pass
raise e raise e
@ -165,6 +178,7 @@ def remove(api: 'client.OpenNebulaClient', templateId: str) -> None:
except Exception: except Exception:
logger.error('Removing template on OpenNebula') logger.error('Removing template on OpenNebula')
def deployFrom(api: 'client.OpenNebulaClient', templateId: str, name: str) -> str: def deployFrom(api: 'client.OpenNebulaClient', templateId: str, name: str) -> str:
""" """
Deploys a virtual machine on selected cluster from selected template Deploys a virtual machine on selected cluster from selected template
@ -177,9 +191,12 @@ def deployFrom(api: 'client.OpenNebulaClient', templateId: str, name: str) -> st
Returns: Returns:
Id of the machine being created form template Id of the machine being created form template
""" """
vmId = api.instantiateTemplate(templateId, name, False, '', False) # api.call('template.instantiate', int(templateId), name, False, '') vmId = api.instantiateTemplate(
templateId, name, False, '', False
) # api.call('template.instantiate', int(templateId), name, False, '')
return vmId return vmId
def checkPublished(api: 'client.OpenNebulaClient', templateId): def checkPublished(api: 'client.OpenNebulaClient', templateId):
""" """
checks if the template is fully published (images are ready...) checks if the template is fully published (images are ready...)
@ -209,7 +226,9 @@ def checkPublished(api: 'client.OpenNebulaClient', templateId):
if state in (types.ImageState.INIT, types.ImageState.LOCKED): if state in (types.ImageState.INIT, types.ImageState.LOCKED):
return False return False
if state != types.ImageState.READY: # If error is not READY if state != types.ImageState.READY: # If error is not READY
raise Exception('Error publishing. Image is in an invalid state. (Check it and delete it if not needed anymore)') raise Exception(
'Error publishing. Image is in an invalid state. (Check it and delete it if not needed anymore)'
)
# Ensure image is non persistent. This may be invoked more than once, but it does not matters # Ensure image is non persistent. This may be invoked more than once, but it does not matters
api.makePersistentImage(imgId, False) api.makePersistentImage(imgId, False)

View File

@ -32,6 +32,7 @@
import enum import enum
import typing import typing
class VmState(enum.Enum): # pylint: disable=too-few-public-methods class VmState(enum.Enum): # pylint: disable=too-few-public-methods
INIT = 0 INIT = 0
PENDING = 1 PENDING = 1

View File

@ -58,7 +58,9 @@ def getMachineState(api: 'client.OpenNebulaClient', machineId: str) -> types.VmS
try: try:
return api.getVMState(machineId) return api.getVMState(machineId)
except Exception as e: except Exception as e:
logger.error('Error obtaining machine state for %s on OpenNebula: %s', machineId, e) logger.error(
'Error obtaining machine state for %s on OpenNebula: %s', machineId, e
)
return types.VmState.UNKNOWN return types.VmState.UNKNOWN
@ -70,7 +72,9 @@ def getMachineSubstate(api: 'client.OpenNebulaClient', machineId: str) -> int:
try: try:
return api.getVMSubstate(machineId) return api.getVMSubstate(machineId)
except Exception as e: except Exception as e:
logger.error('Error obtaining machine substate for %s on OpenNebula: %s', machineId, e) logger.error(
'Error obtaining machine substate for %s on OpenNebula: %s', machineId, e
)
return types.VmState.UNKNOWN.value return types.VmState.UNKNOWN.value
@ -122,6 +126,7 @@ def suspendMachine(api: 'client.OpenNebulaClient', machineId: str) -> None:
except Exception as e: except Exception as e:
logger.error('Error suspending %s on OpenNebula: %s', machineId, e) logger.error('Error suspending %s on OpenNebula: %s', machineId, e)
def shutdownMachine(api: 'client.OpenNebulaClient', machineId: str) -> None: def shutdownMachine(api: 'client.OpenNebulaClient', machineId: str) -> None:
''' '''
Tries to "gracefully" shutdown a machine. No check is done, it is simply requested to OpenNebula Tries to "gracefully" shutdown a machine. No check is done, it is simply requested to OpenNebula
@ -171,7 +176,9 @@ def removeMachine(api: 'client.OpenNebulaClient', machineId: str) -> None:
raise Exception(err) raise Exception(err)
def enumerateMachines(api: 'client.OpenNebulaClient') -> typing.Iterable[types.VirtualMachineType]: def enumerateMachines(
api: 'client.OpenNebulaClient',
) -> typing.Iterable[types.VirtualMachineType]:
''' '''
Obtains the list of machines inside OpenNebula. Obtains the list of machines inside OpenNebula.
Machines starting with UDS are filtered out Machines starting with UDS are filtered out
@ -189,7 +196,11 @@ def enumerateMachines(api: 'client.OpenNebulaClient') -> typing.Iterable[types.V
yield from api.enumVMs() yield from api.enumVMs()
def getNetInfo(api: 'client.OpenNebulaClient', machineId: str, networkId: typing.Optional[str] = None) -> typing.Tuple[str, str]: def getNetInfo(
api: 'client.OpenNebulaClient',
machineId: str,
networkId: typing.Optional[str] = None,
) -> typing.Tuple[str, str]:
''' '''
Get the MAC and the IP for the network and machine. If network is None, for the first network Get the MAC and the IP for the network and machine. If network is None, for the first network
''' '''
@ -204,7 +215,9 @@ def getNetInfo(api: 'client.OpenNebulaClient', machineId: str, networkId: typing
node = nic node = nic
break break
except Exception: except Exception:
raise Exception('No network interface found on template. Please, add a network and republish.') raise Exception(
'No network interface found on template. Please, add a network and republish.'
)
logger.debug(node.toxml()) logger.debug(node.toxml())
@ -217,10 +230,14 @@ def getNetInfo(api: 'client.OpenNebulaClient', machineId: str, networkId: typing
return (node.getElementsByTagName('MAC')[0].childNodes[0].data, ip) return (node.getElementsByTagName('MAC')[0].childNodes[0].data, ip)
except Exception: except Exception:
raise Exception('No network interface found on template. Please, add a network and republish.') raise Exception(
'No network interface found on template. Please, add a network and republish.'
)
def getDisplayConnection(api: 'client.OpenNebulaClient', machineId: str) -> typing.Optional[typing.Dict[str, typing.Any]]: def getDisplayConnection(
api: 'client.OpenNebulaClient', machineId: str
) -> typing.Optional[typing.Dict[str, typing.Any]]:
''' '''
If machine is not running or there is not a display, will return NONE If machine is not running or there is not a display, will return NONE
SPICE connections should check that 'type' is 'SPICE' SPICE connections should check that 'type' is 'SPICE'
@ -236,17 +253,18 @@ def getDisplayConnection(api: 'client.OpenNebulaClient', machineId: str) -> typi
except Exception: except Exception:
passwd = '' passwd = ''
host = md.getElementsByTagName('HISTORY_RECORDS')[0].lastChild.getElementsByTagName('HOSTNAME')[0].childNodes[0].data host = (
return { md.getElementsByTagName('HISTORY_RECORDS')[0]
'type': type_, .lastChild.getElementsByTagName('HOSTNAME')[0]
'host': host, .childNodes[0]
'port': int(port), .data
'passwd': passwd )
} return {'type': type_, 'host': host, 'port': int(port), 'passwd': passwd}
except Exception: except Exception:
return None # No SPICE connection return None # No SPICE connection
# Sample NIC Content (there will be as much as nics) # Sample NIC Content (there will be as much as nics)
# <NIC> # <NIC>
# <BRIDGE><![CDATA[br0]]></BRIDGE> # <BRIDGE><![CDATA[br0]]></BRIDGE>

View File

@ -74,16 +74,72 @@ class OpenNebulaProvider(ServiceProvider): # pylint: disable=too-many-public-me
# but used for sample purposes # but used for sample purposes
# If we don't indicate an order, the output order of fields will be # If we don't indicate an order, the output order of fields will be
# "random" # "random"
host = gui.TextField(length=64, label=_('Host'), order=1, tooltip=_('OpenNebula Host'), required=True) host = gui.TextField(
port = gui.NumericField(length=5, label=_('Port'), defvalue='2633', order=2, tooltip=_('OpenNebula Port (default is 2633 for non ssl connection)'), required=True) length=64, label=_('Host'), order=1, tooltip=_('OpenNebula Host'), required=True
ssl = gui.CheckBoxField(label=_('Use SSL'), order=3, tooltip=_('If checked, the connection will be forced to be ssl (will not work if server is not providing ssl)')) )
username = gui.TextField(length=32, label=_('Username'), order=4, tooltip=_('User with valid privileges on OpenNebula'), required=True, defvalue='oneadmin') port = gui.NumericField(
password = gui.PasswordField(lenth=32, label=_('Password'), order=5, tooltip=_('Password of the user of OpenNebula'), required=True) length=5,
label=_('Port'),
defvalue='2633',
order=2,
tooltip=_('OpenNebula Port (default is 2633 for non ssl connection)'),
required=True,
)
ssl = gui.CheckBoxField(
label=_('Use SSL'),
order=3,
tooltip=_(
'If checked, the connection will be forced to be ssl (will not work if server is not providing ssl)'
),
)
username = gui.TextField(
length=32,
label=_('Username'),
order=4,
tooltip=_('User with valid privileges on OpenNebula'),
required=True,
defvalue='oneadmin',
)
password = gui.PasswordField(
lenth=32,
label=_('Password'),
order=5,
tooltip=_('Password of the user of OpenNebula'),
required=True,
)
maxPreparingServices = gui.NumericField(length=3, label=_('Creation concurrency'), defvalue='10', minValue=1, maxValue=65536, order=50, tooltip=_('Maximum number of concurrently creating VMs'), required=True, tab=gui.ADVANCED_TAB) maxPreparingServices = gui.NumericField(
maxRemovingServices = gui.NumericField(length=3, label=_('Removal concurrency'), defvalue='5', minValue=1, maxValue=65536, order=51, tooltip=_('Maximum number of concurrently removing VMs'), required=True, tab=gui.ADVANCED_TAB) length=3,
label=_('Creation concurrency'),
defvalue='10',
minValue=1,
maxValue=65536,
order=50,
tooltip=_('Maximum number of concurrently creating VMs'),
required=True,
tab=gui.ADVANCED_TAB,
)
maxRemovingServices = gui.NumericField(
length=3,
label=_('Removal concurrency'),
defvalue='5',
minValue=1,
maxValue=65536,
order=51,
tooltip=_('Maximum number of concurrently removing VMs'),
required=True,
tab=gui.ADVANCED_TAB,
)
timeout = gui.NumericField(length=3, label=_('Timeout'), defvalue='10', order=90, tooltip=_('Timeout in seconds of connection to OpenNebula'), required=True, tab=gui.ADVANCED_TAB) timeout = gui.NumericField(
length=3,
label=_('Timeout'),
defvalue='10',
order=90,
tooltip=_('Timeout in seconds of connection to OpenNebula'),
required=True,
tab=gui.ADVANCED_TAB,
)
# Own variables # Own variables
_api: typing.Optional[on.client.OpenNebulaClient] = None _api: typing.Optional[on.client.OpenNebulaClient] = None
@ -102,12 +158,16 @@ class OpenNebulaProvider(ServiceProvider): # pylint: disable=too-many-public-me
@property @property
def endpoint(self) -> str: def endpoint(self) -> str:
return 'http{}://{}:{}/RPC2'.format('s' if self.ssl.isTrue() else '', self.host.value, self.port.value) return 'http{}://{}:{}/RPC2'.format(
's' if self.ssl.isTrue() else '', self.host.value, self.port.value
)
@property @property
def api(self) -> on.client.OpenNebulaClient: def api(self) -> on.client.OpenNebulaClient:
if self._api is None: if self._api is None:
self._api = on.client.OpenNebulaClient(self.username.value, self.password.value, self.endpoint) self._api = on.client.OpenNebulaClient(
self.username.value, self.password.value, self.endpoint
)
return self._api return self._api
@ -128,16 +188,23 @@ class OpenNebulaProvider(ServiceProvider): # pylint: disable=too-many-public-me
try: try:
if self.api.version[0] < '4': if self.api.version[0] < '4':
return [False, 'OpenNebula version is not supported (required version 4.1 or newer)'] return [
False,
'OpenNebula version is not supported (required version 4.1 or newer)',
]
except Exception as e: except Exception as e:
return [False, '{}'.format(e)] return [False, '{}'.format(e)]
return [True, _('Opennebula test connection passed')] return [True, _('Opennebula test connection passed')]
def getDatastores(self, datastoreType: int = 0) -> typing.Iterable[on.types.StorageType]: def getDatastores(
self, datastoreType: int = 0
) -> typing.Iterable[on.types.StorageType]:
yield from on.storage.enumerateDatastores(self.api, datastoreType) yield from on.storage.enumerateDatastores(self.api, datastoreType)
def getTemplates(self, force: bool = False) -> typing.Iterable[on.types.TemplateType]: def getTemplates(
self, force: bool = False
) -> typing.Iterable[on.types.TemplateType]:
yield from on.template.getTemplates(self.api, force) yield from on.template.getTemplates(self.api, force)
def makeTemplate(self, fromTemplateId: str, name, toDataStore: str) -> str: def makeTemplate(self, fromTemplateId: str, name, toDataStore: str) -> str:
@ -234,7 +301,9 @@ class OpenNebulaProvider(ServiceProvider): # pylint: disable=too-many-public-me
''' '''
on.vm.removeMachine(self.api, machineId) on.vm.removeMachine(self.api, machineId)
def getNetInfo(self, machineId: str, networkId: typing.Optional[str] = None) -> typing.Tuple[str, str]: def getNetInfo(
self, machineId: str, networkId: typing.Optional[str] = None
) -> typing.Tuple[str, str]:
''' '''
Changes the mac address of first nic of the machine to the one specified Changes the mac address of first nic of the machine to the one specified
''' '''
@ -253,17 +322,14 @@ class OpenNebulaProvider(ServiceProvider): # pylint: disable=too-many-public-me
'secure_port': -1, 'secure_port': -1,
'monitors': 1, 'monitors': 1,
'cert_subject': '', 'cert_subject': '',
'ticket': { 'ticket': {'value': display['passwd'], 'expiry': ''},
'value': display['passwd'],
'expiry': ''
}
} }
def desktopLogin(self, machineId: str, username: str, password: str, domain: str): def desktopLogin(self, machineId: str, username: str, password: str, domain: str) -> typing.Dict[str, typing.Any]:
''' '''
Not provided by OpenNebula API right now Not provided by OpenNebula API right now
''' '''
return return dict()
@staticmethod @staticmethod
def test(env: 'Environment', data: 'Module.ValuesType') -> typing.List[typing.Any]: def test(env: 'Environment', data: 'Module.ValuesType') -> typing.List[typing.Any]:

View File

@ -38,7 +38,7 @@ from uds.core.util.state import State
# Not imported at runtime, just for type checking # Not imported at runtime, just for type checking
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from . import service from .service import LiveService
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -48,21 +48,25 @@ class LivePublication(Publication):
This class provides the publication of a oVirtLinkedService This class provides the publication of a oVirtLinkedService
""" """
suggestedTime = 2 # : Suggested recheck time if publication is unfinished in seconds suggestedTime = (
2 # : Suggested recheck time if publication is unfinished in seconds
)
_name: str = '' _name: str = ''
_reason: str = '' _reason: str = ''
_templateId: str = '' _templateId: str = ''
_state: str = 'r' _state: str = 'r'
def service(self) -> 'service.LiveService': def service(self) -> 'LiveService':
return typing.cast('service.LiveService', super().service()) return typing.cast('LiveService', super().service())
def marshal(self) -> bytes: def marshal(self) -> bytes:
""" """
returns data from an instance of Sample Publication serialized returns data from an instance of Sample Publication serialized
""" """
return '\t'.join(['v1', self._name, self._reason, self._templateId, self._state]).encode('utf8') return '\t'.join(
['v1', self._name, self._reason, self._templateId, self._state]
).encode('utf8')
def unmarshal(self, data: bytes) -> None: def unmarshal(self, data: bytes) -> None:
""" """
@ -77,7 +81,9 @@ class LivePublication(Publication):
""" """
Realizes the publication of the service Realizes the publication of the service
""" """
self._name = self.service().sanitizeVmName('UDSP ' + self.dsName() + "-" + str(self.revision())) self._name = self.service().sanitizeVmName(
'UDSP ' + self.dsName() + "-" + str(self.revision())
)
self._reason = '' # No error, no reason for it self._reason = '' # No error, no reason for it
self._state = 'running' self._state = 'running'

View File

@ -55,6 +55,7 @@ class LiveService(Service):
""" """
Opennebula Live Service Opennebula Live Service
""" """
# : Name to show the administrator. This string will be translated BEFORE # : Name to show the administrator. This string will be translated BEFORE
# : sending it to administration interface, so don't forget to # : sending it to administration interface, so don't forget to
# : mark it as _ (using ugettext_noop) # : mark it as _ (using ugettext_noop)
@ -109,7 +110,7 @@ class LiveService(Service):
label=_("Datastore"), label=_("Datastore"),
order=100, order=100,
tooltip=_('Service clones datastore'), tooltip=_('Service clones datastore'),
required=True required=True,
) )
template = gui.ChoiceField( template = gui.ChoiceField(
@ -117,7 +118,7 @@ class LiveService(Service):
order=110, order=110,
tooltip=_('Service base template'), tooltip=_('Service base template'),
tab=_('Machine'), tab=_('Machine'),
required=True required=True,
) )
baseName = gui.TextField( baseName = gui.TextField(
@ -126,7 +127,7 @@ class LiveService(Service):
order=111, order=111,
tooltip=_('Base name for clones from this machine'), tooltip=_('Base name for clones from this machine'),
tab=_('Machine'), tab=_('Machine'),
required=True required=True,
) )
lenName = gui.NumericField( lenName = gui.NumericField(
@ -136,7 +137,7 @@ class LiveService(Service):
order=112, order=112,
tooltip=_('Size of numeric part for the names of these machines'), tooltip=_('Size of numeric part for the names of these machines'),
tab=_('Machine'), tab=_('Machine'),
required=True required=True,
) )
def initialize(self, values: 'Module.ValuesType') -> None: def initialize(self, values: 'Module.ValuesType') -> None:
@ -149,7 +150,9 @@ class LiveService(Service):
if not values: if not values:
return return
self.baseName.value = validators.validateHostname(self.baseName.value, maxLength=15-self.lenName.num(), asPattern=True) self.baseName.value = validators.validateHostname(
self.baseName.value, maxLength=15 - self.lenName.num(), asPattern=True
)
def parent(self) -> 'OpenNebulaProvider': def parent(self) -> 'OpenNebulaProvider':
return typing.cast('OpenNebulaProvider', super().parent()) return typing.cast('OpenNebulaProvider', super().parent())
@ -173,7 +176,9 @@ class LiveService(Service):
return self.parent().sanitizeVmName(name) return self.parent().sanitizeVmName(name)
def makeTemplate(self, templateName: str) -> str: def makeTemplate(self, templateName: str) -> str:
return self.parent().makeTemplate(self.template.value, templateName, self.datastore.value) return self.parent().makeTemplate(
self.template.value, templateName, self.datastore.value
)
def checkTemplatePublished(self, templateId: str) -> bool: def checkTemplatePublished(self, templateId: str) -> bool:
return self.parent().checkTemplatePublished(templateId) return self.parent().checkTemplatePublished(templateId)
@ -288,7 +293,9 @@ class LiveService(Service):
""" """
self.parent().removeMachine(machineId) self.parent().removeMachine(machineId)
def getNetInfo(self, machineId: str, networkId: typing.Optional[str] = None) -> typing.Tuple[str, str]: def getNetInfo(
self, machineId: str, networkId: typing.Optional[str] = None
) -> typing.Tuple[str, str]:
""" """
Changes the mac address of first nic of the machine to the one specified Changes the mac address of first nic of the machine to the one specified
""" """
@ -309,5 +316,7 @@ class LiveService(Service):
def getConsoleConnection(self, machineId: str) -> typing.Dict[str, typing.Any]: def getConsoleConnection(self, machineId: str) -> typing.Dict[str, typing.Any]:
return self.parent().getConsoleConnection(machineId) return self.parent().getConsoleConnection(machineId)
def desktopLogin(self, machineId: str, username: str, password: str, domain: str) -> typing.Dict[str, typing.Any]: def desktopLogin(
self, machineId: str, username: str, password: str, domain: str
) -> typing.Dict[str, typing.Any]:
return self.parent().desktopLogin(machineId, username, password, domain) return self.parent().desktopLogin(machineId, username, password, domain)

View File

@ -64,6 +64,7 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods
The logic for managing ovirt deployments (user machines in this case) is here. The logic for managing ovirt deployments (user machines in this case) is here.
""" """
_name: str = '' _name: str = ''
_ip: str = '' _ip: str = ''
_mac: str = '' _mac: str = ''
@ -92,15 +93,17 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods
# Serializable needed methods # Serializable needed methods
def marshal(self) -> bytes: def marshal(self) -> bytes:
return b'\1'.join([ return b'\1'.join(
[
b'v1', b'v1',
self._name.encode('utf8'), self._name.encode('utf8'),
self._ip.encode('utf8'), self._ip.encode('utf8'),
self._mac.encode('utf8'), self._mac.encode('utf8'),
self._vmid.encode('utf8'), self._vmid.encode('utf8'),
self._reason.encode('utf8'), self._reason.encode('utf8'),
pickle.dumps(self._queue, protocol=0) pickle.dumps(self._queue, protocol=0),
]) ]
)
def unmarshal(self, data: bytes) -> None: def unmarshal(self, data: bytes) -> None:
vals = data.split(b'\1') vals = data.split(b'\1')
@ -115,7 +118,9 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods
def getName(self) -> str: def getName(self) -> str:
if self._name == '': if self._name == '':
try: try:
self._name = self.nameGenerator().get(self.service().getBaseName(), self.service().getLenName()) self._name = self.nameGenerator().get(
self.service().getBaseName(), self.service().getLenName()
)
except KeyError: except KeyError:
return NO_MORE_NAMES return NO_MORE_NAMES
return self._name return self._name
@ -188,7 +193,12 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods
self._queue = [opCreate, opWait, opSuspend, opFinish] self._queue = [opCreate, opWait, opSuspend, opFinish]
def __checkMachineState(self, chkState: str) -> str: def __checkMachineState(self, chkState: str) -> str:
logger.debug('Checking that state of machine %s (%s) is %s', self._vmid, self._name, chkState) logger.debug(
'Checking that state of machine %s (%s) is %s',
self._vmid,
self._name,
chkState,
)
status = self.service().getMachineState(self._vmid) status = self.service().getMachineState(self._vmid)
# If we want to check an state and machine does not exists (except in case that we whant to check this) # If we want to check an state and machine does not exists (except in case that we whant to check this)
@ -263,7 +273,9 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods
try: try:
if op not in fncs: if op not in fncs:
return self.__error('Unknown operation found at execution queue ({0})'.format(op)) return self.__error(
'Unknown operation found at execution queue ({0})'.format(op)
)
fncs[op]() fncs[op]()
@ -295,9 +307,13 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods
templateId = self.publication().getTemplateId() templateId = self.publication().getTemplateId()
name = self.getName() name = self.getName()
if name == NO_MORE_NAMES: if name == NO_MORE_NAMES:
raise Exception('No more names available for this service. (Increase digits for this service to fix)') raise Exception(
'No more names available for this service. (Increase digits for this service to fix)'
)
name = self.service().sanitizeVmName(name) # OpenNebula don't let us to create machines with more than 15 chars!!! name = self.service().sanitizeVmName(
name
) # OpenNebula don't let us to create machines with more than 15 chars!!!
self._vmid = self.service().deployFromTemplate(name, templateId) self._vmid = self.service().deployFromTemplate(name, templateId)
if self._vmid is None: if self._vmid is None:
@ -388,7 +404,9 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods
try: try:
if op not in fncs: if op not in fncs:
return self.__error('Unknown operation found at execution queue ({0})'.format(op)) return self.__error(
'Unknown operation found at execution queue ({0})'.format(op)
)
state = fncs[op]() state = fncs[op]()
if state == State.FINISHED: if state == State.FINISHED:
@ -462,4 +480,12 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods
}.get(op, '????') }.get(op, '????')
def __debug(self, txt: str) -> None: def __debug(self, txt: str) -> None:
logger.debug('State at %s: name: %s, ip: %s, mac: %s, vmid:%s, queue: %s', txt, self._name, self._ip, self._mac, self._vmid, [LiveDeployment.__op2str(op) for op in self._queue]) logger.debug(
'State at %s: name: %s, ip: %s, mac: %s, vmid:%s, queue: %s',
txt,
self._name,
self._ip,
self._mac,
self._vmid,
[LiveDeployment.__op2str(op) for op in self._queue],
)

View File

@ -39,6 +39,7 @@ from . import openstack
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def getApi(parameters: typing.Dict[str, str]) -> typing.Tuple[openstack.Client, bool]: def getApi(parameters: typing.Dict[str, str]) -> typing.Tuple[openstack.Client, bool]:
from .provider_legacy import ProviderLegacy from .provider_legacy import ProviderLegacy
from .provider import OpenStackProvider from .provider import OpenStackProvider
@ -61,17 +62,26 @@ def getApi(parameters: typing.Dict[str, str]) -> typing.Tuple[openstack.Client,
return (provider.api(parameters['project'], parameters['region']), useSubnetsName) return (provider.api(parameters['project'], parameters['region']), useSubnetsName)
def getResources(parameters: typing.Dict[str, str]) -> typing.List[typing.Dict[str, typing.Any]]: def getResources(
parameters: typing.Dict[str, str]
) -> typing.List[typing.Dict[str, typing.Any]]:
''' '''
This helper is designed as a callback for Project Selector This helper is designed as a callback for Project Selector
''' '''
api, nameFromSubnets = getApi(parameters) api, nameFromSubnets = getApi(parameters)
zones = [gui.choiceItem(z, z) for z in api.listAvailabilityZones()] zones = [gui.choiceItem(z, z) for z in api.listAvailabilityZones()]
networks = [gui.choiceItem(z['id'], z['name']) for z in api.listNetworks(nameFromSubnets=nameFromSubnets)] networks = [
gui.choiceItem(z['id'], z['name'])
for z in api.listNetworks(nameFromSubnets=nameFromSubnets)
]
flavors = [gui.choiceItem(z['id'], z['name']) for z in api.listFlavors()] flavors = [gui.choiceItem(z['id'], z['name']) for z in api.listFlavors()]
securityGroups = [gui.choiceItem(z['id'], z['name']) for z in api.listSecurityGroups()] securityGroups = [
volumeTypes = [gui.choiceItem('-', _('None'))] + [gui.choiceItem(t['id'], t['name']) for t in api.listVolumeTypes()] gui.choiceItem(z['id'], z['name']) for z in api.listSecurityGroups()
]
volumeTypes = [gui.choiceItem('-', _('None'))] + [
gui.choiceItem(t['id'], t['name']) for t in api.listVolumeTypes()
]
data = [ data = [
{'name': 'availabilityZone', 'values': zones}, {'name': 'availabilityZone', 'values': zones},
@ -83,14 +93,19 @@ def getResources(parameters: typing.Dict[str, str]) -> typing.List[typing.Dict[s
logger.debug('Return data: %s', data) logger.debug('Return data: %s', data)
return data return data
def getVolumes(parameters: typing.Dict[str, str]) -> typing.List[typing.Dict[str, typing.Any]]:
def getVolumes(
parameters: typing.Dict[str, str]
) -> typing.List[typing.Dict[str, typing.Any]]:
''' '''
This helper is designed as a callback for Zone Selector This helper is designed as a callback for Zone Selector
''' '''
api, _ = getApi(parameters) api, _ = getApi(parameters)
# Source volumes are all available for us # Source volumes are all available for us
# volumes = [gui.choiceItem(v['id'], v['name']) for v in api.listVolumes() if v['name'] != '' and v['availability_zone'] == parameters['availabilityZone']] # volumes = [gui.choiceItem(v['id'], v['name']) for v in api.listVolumes() if v['name'] != '' and v['availability_zone'] == parameters['availabilityZone']]
volumes = [gui.choiceItem(v['id'], v['name']) for v in api.listVolumes() if v['name'] != ''] volumes = [
gui.choiceItem(v['id'], v['name']) for v in api.listVolumes() if v['name'] != ''
]
data = [ data = [
{'name': 'volume', 'values': volumes}, {'name': 'volume', 'values': volumes},

View File

@ -38,17 +38,45 @@ import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
( (
ACTIVE, BUILDING, DELETED, ERROR, ACTIVE,
HARD_REBOOT, MIGRATING, PASSWORD, BUILDING,
PAUSED, REBOOT, REBUILD, RESCUED, DELETED,
RESIZED, REVERT_RESIZE, SOFT_DELETED, ERROR,
STOPPED, SUSPENDED, UNKNOWN, VERIFY_RESIZE, SHUTOFF HARD_REBOOT,
MIGRATING,
PASSWORD,
PAUSED,
REBOOT,
REBUILD,
RESCUED,
RESIZED,
REVERT_RESIZE,
SOFT_DELETED,
STOPPED,
SUSPENDED,
UNKNOWN,
VERIFY_RESIZE,
SHUTOFF,
) = ( ) = (
'ACTIVE', 'BUILDING', 'DELETED', 'ERROR', 'ACTIVE',
'HARD_REBOOT', 'MIGRATING', 'PASSWORD', 'BUILDING',
'PAUSED', 'REBOOT', 'REBUILD', 'RESCUED', 'DELETED',
'RESIZED', 'REVERT_RESIZE', 'SOFT_DELETED', 'ERROR',
'STOPPED', 'SUSPENDED', 'UNKNOWN', 'VERIFY_RESIZE', 'SHUTOFF' 'HARD_REBOOT',
'MIGRATING',
'PASSWORD',
'PAUSED',
'REBOOT',
'REBUILD',
'RESCUED',
'RESIZED',
'REVERT_RESIZE',
'SOFT_DELETED',
'STOPPED',
'SUSPENDED',
'UNKNOWN',
'VERIFY_RESIZE',
'SHUTOFF',
) )

View File

@ -281,7 +281,6 @@ class Client: # pylint: disable=too-many-public-methods
else: else:
self._volume = 'volumev2' self._volume = 'volumev2'
def ensureAuthenticated(self) -> None: def ensureAuthenticated(self) -> None:
if ( if (
self._authenticated is False self._authenticated is False

View File

@ -208,7 +208,9 @@ class OpenStackProvider(ServiceProvider):
length=96, length=96,
label=_('Proxy'), label=_('Proxy'),
order=91, order=91,
tooltip=_('Proxy used for connection to azure for HTTPS connections (use PROTOCOL://host:port, i.e. http://10.10.0.1:8080)'), tooltip=_(
'Proxy used for connection to azure for HTTPS connections (use PROTOCOL://host:port, i.e. http://10.10.0.1:8080)'
),
required=False, required=False,
tab=gui.ADVANCED_TAB, tab=gui.ADVANCED_TAB,
) )
@ -233,9 +235,7 @@ class OpenStackProvider(ServiceProvider):
if self._api is None: if self._api is None:
proxies = None proxies = None
if self.httpsProxy.value.strip(): if self.httpsProxy.value.strip():
proxies = { proxies = {'https': self.httpsProxy.value}
'https': self.httpsProxy.value
}
self._api = openstack.Client( self._api = openstack.Client(
self.endpoint.value, self.endpoint.value,
-1, -1,
@ -247,7 +247,7 @@ class OpenStackProvider(ServiceProvider):
projectId=projectId, projectId=projectId,
region=region, region=region,
access=self.access.value, access=self.access.value,
proxies=proxies proxies=proxies,
) )
return self._api return self._api

View File

@ -194,7 +194,9 @@ class ProviderLegacy(ServiceProvider):
length=96, length=96,
label=_('Proxy'), label=_('Proxy'),
order=91, order=91,
tooltip=_('Proxy used for connection to azure for HTTPS connections (use PROTOCOL://host:port, i.e. http://10.10.0.1:8080)'), tooltip=_(
'Proxy used for connection to azure for HTTPS connections (use PROTOCOL://host:port, i.e. http://10.10.0.1:8080)'
),
required=False, required=False,
tab=gui.ADVANCED_TAB, tab=gui.ADVANCED_TAB,
) )

View File

@ -47,13 +47,16 @@ class LivePublication(Publication):
""" """
This class provides the publication of a oVirtLinkedService This class provides the publication of a oVirtLinkedService
""" """
_name: str = '' _name: str = ''
_reason: str = '' _reason: str = ''
_templateId: str = '' _templateId: str = ''
_state: str = 'r' _state: str = 'r'
_destroyAfter: str = 'n' _destroyAfter: str = 'n'
suggestedTime = 20 # : Suggested recheck time if publication is unfinished in seconds suggestedTime = (
20 # : Suggested recheck time if publication is unfinished in seconds
)
def initialize(self): def initialize(self):
""" """
@ -78,7 +81,16 @@ class LivePublication(Publication):
""" """
returns data from an instance of Sample Publication serialized returns data from an instance of Sample Publication serialized
""" """
return '\t'.join(['v1', self._name, self._reason, self._templateId, self._state, self._destroyAfter]).encode('utf8') return '\t'.join(
[
'v1',
self._name,
self._reason,
self._templateId,
self._state,
self._destroyAfter,
]
).encode('utf8')
def unmarshal(self, data: bytes) -> None: def unmarshal(self, data: bytes) -> None:
""" """
@ -86,13 +98,21 @@ class LivePublication(Publication):
""" """
vals = data.decode('utf8').split('\t') vals = data.decode('utf8').split('\t')
if vals[0] == 'v1': if vals[0] == 'v1':
self._name, self._reason, self._templateId, self._state, self._destroyAfter = vals[1:] (
self._name,
self._reason,
self._templateId,
self._state,
self._destroyAfter,
) = vals[1:]
def publish(self) -> str: def publish(self) -> str:
""" """
Realizes the publication of the service Realizes the publication of the service
""" """
self._name = self.service().sanitizeVmName('UDSP ' + self.dsName() + "-" + str(self.revision())) self._name = self.service().sanitizeVmName(
'UDSP ' + self.dsName() + "-" + str(self.revision())
)
self._reason = '' # No error, no reason for it self._reason = '' # No error, no reason for it
self._destroyAfter = 'n' self._destroyAfter = 'n'
@ -118,7 +138,9 @@ class LivePublication(Publication):
if self._state == 'available': if self._state == 'available':
return State.FINISHED return State.FINISHED
self._state = self.service().getTemplate(self._templateId)['status'] # For next check self._state = self.service().getTemplate(self._templateId)[
'status'
] # For next check
if self._destroyAfter == 'y' and self._state == 'available': if self._destroyAfter == 'y' and self._state == 'available':
return self.destroy() return self.destroy()

View File

@ -51,6 +51,7 @@ if typing.TYPE_CHECKING:
from . import openstack from . import openstack
from .provider import OpenStackProvider from .provider import OpenStackProvider
from .provider_legacy import ProviderLegacy from .provider_legacy import ProviderLegacy
Provider = typing.Union[OpenStackProvider, ProviderLegacy] Provider = typing.Union[OpenStackProvider, ProviderLegacy]
@ -58,6 +59,7 @@ class LiveService(Service):
""" """
OpenStack Live Service OpenStack Live Service
""" """
# : Name to show the administrator. This string will be translated BEFORE # : Name to show the administrator. This string will be translated BEFORE
# : sending it to administration interface, so don't forget to # : sending it to administration interface, so don't forget to
# : mark it as _ (using ugettext_noop) # : mark it as _ (using ugettext_noop)
@ -104,18 +106,24 @@ class LiveService(Service):
servicesTypeProvided = (serviceTypes.VDI,) servicesTypeProvided = (serviceTypes.VDI,)
# Now the form part # Now the form part
region = gui.ChoiceField(label=_('Region'), order=1, tooltip=_('Service region'), required=True, rdonly=True) region = gui.ChoiceField(
label=_('Region'),
order=1,
tooltip=_('Service region'),
required=True,
rdonly=True,
)
project = gui.ChoiceField( project = gui.ChoiceField(
label=_('Project'), label=_('Project'),
order=2, order=2,
fills={ fills={
'callbackName': 'osFillResources', 'callbackName': 'osFillResources',
'function': helpers.getResources, 'function': helpers.getResources,
'parameters' : ['ov', 'ev', 'project', 'region', 'legacy'] 'parameters': ['ov', 'ev', 'project', 'region', 'legacy'],
}, },
tooltip=_('Project for this service'), tooltip=_('Project for this service'),
required=True, required=True,
rdonly=True rdonly=True,
) )
availabilityZone = gui.ChoiceField( availabilityZone = gui.ChoiceField(
label=_('Availability Zones'), label=_('Availability Zones'),
@ -123,18 +131,49 @@ class LiveService(Service):
fills={ fills={
'callbackName': 'osFillVolumees', 'callbackName': 'osFillVolumees',
'function': helpers.getVolumes, 'function': helpers.getVolumes,
'parameters' : ['ov', 'ev', 'project', 'region', 'availabilityZone', 'legacy'] 'parameters': [
'ov',
'ev',
'project',
'region',
'availabilityZone',
'legacy',
],
}, },
tooltip=_('Service availability zones'), tooltip=_('Service availability zones'),
required=True, required=True,
rdonly=True rdonly=True,
)
volume = gui.ChoiceField(
label=_('Volume'),
order=4,
tooltip=_('Base volume for service (restricted by availability zone)'),
required=True,
tab=_('Machine'),
) )
volume = gui.ChoiceField(label=_('Volume'), order=4, tooltip=_('Base volume for service (restricted by availability zone)'), required=True, tab=_('Machine'))
# volumeType = gui.ChoiceField(label=_('Volume Type'), order=5, tooltip=_('Volume type for service'), required=True) # volumeType = gui.ChoiceField(label=_('Volume Type'), order=5, tooltip=_('Volume type for service'), required=True)
network = gui.ChoiceField(label=_('Network'), order=6, tooltip=_('Network to attach to this service'), required=True, tab=_('Machine')) network = gui.ChoiceField(
flavor = gui.ChoiceField(label=_('Flavor'), order=7, tooltip=_('Flavor for service'), required=True, tab=_('Machine')) label=_('Network'),
order=6,
tooltip=_('Network to attach to this service'),
required=True,
tab=_('Machine'),
)
flavor = gui.ChoiceField(
label=_('Flavor'),
order=7,
tooltip=_('Flavor for service'),
required=True,
tab=_('Machine'),
)
securityGroups = gui.MultiChoiceField(label=_('Security Groups'), order=8, tooltip=_('Service security groups'), required=True, tab=_('Machine')) securityGroups = gui.MultiChoiceField(
label=_('Security Groups'),
order=8,
tooltip=_('Service security groups'),
required=True,
tab=_('Machine'),
)
baseName = gui.TextField( baseName = gui.TextField(
label=_('Machine Names'), label=_('Machine Names'),
@ -142,7 +181,7 @@ class LiveService(Service):
order=9, order=9,
tooltip=_('Base name for clones from this machine'), tooltip=_('Base name for clones from this machine'),
required=True, required=True,
tab=_('Machine') tab=_('Machine'),
) )
lenName = gui.NumericField( lenName = gui.NumericField(
@ -152,12 +191,14 @@ class LiveService(Service):
order=10, order=10,
tooltip=_('Size of numeric part for the names of these machines'), tooltip=_('Size of numeric part for the names of these machines'),
required=True, required=True,
tab=_('Machine') tab=_('Machine'),
) )
ov = gui.HiddenField(value=None) ov = gui.HiddenField(value=None)
ev = gui.HiddenField(value=None) ev = gui.HiddenField(value=None)
legacy = gui.HiddenField(value=None) # We need to keep the env so we can instantiate the Provider legacy = gui.HiddenField(
value=None
) # We need to keep the env so we can instantiate the Provider
_api: typing.Optional['openstack.Client'] = None _api: typing.Optional['openstack.Client'] = None
@ -183,15 +224,26 @@ class LiveService(Service):
""" """
api = self.parent().api() api = self.parent().api()
if not self.parent().legacy and self.parent().region.value: # Checks if legacy or current openstack provider
regions = [gui.choiceItem(self.parent().region.value, self.parent().region.value)] parentCurrent = (
typing.cast('OpenStackProvider', self.parent())
if not self.parent().legacy
else None
)
if parentCurrent and parentCurrent.region.value:
regions = [
gui.choiceItem(parentCurrent.region.value, parentCurrent.region.value)
]
else: else:
regions = [gui.choiceItem(r['id'], r['id']) for r in api.listRegions()] regions = [gui.choiceItem(r['id'], r['id']) for r in api.listRegions()]
self.region.setValues(regions) self.region.setValues(regions)
if not self.parent().legacy and self.parent().tenant.value: if parentCurrent and parentCurrent.tenant.value:
tenants = [gui.choiceItem(self.parent().tenant.value, self.parent().tenant.value)] tenants = [
gui.choiceItem(parentCurrent.tenant.value, parentCurrent.tenant.value)
]
else: else:
tenants = [gui.choiceItem(t['id'], t['name']) for t in api.listProjects()] tenants = [gui.choiceItem(t['id'], t['name']) for t in api.listProjects()]
self.project.setValues(tenants) self.project.setValues(tenants)
@ -206,7 +258,9 @@ class LiveService(Service):
@property @property
def api(self) -> 'openstack.Client': def api(self) -> 'openstack.Client':
if not self._api: if not self._api:
self._api = self.parent().api(projectId=self.project.value, region=self.region.value) self._api = self.parent().api(
projectId=self.project.value, region=self.region.value
)
return self._api return self._api
@ -219,7 +273,9 @@ class LiveService(Service):
# raise Exception('The Volume is in use right now. Ensure that there is no machine running before publishing') # raise Exception('The Volume is in use right now. Ensure that there is no machine running before publishing')
description = description or 'UDS Template snapshot' description = description or 'UDS Template snapshot'
return self.api.createVolumeSnapshot(self.volume.value, templateName, description) return self.api.createVolumeSnapshot(
self.volume.value, templateName, description
)
def getTemplate(self, snapshotId: str): def getTemplate(self, snapshotId: str):
""" """
@ -247,7 +303,7 @@ class LiveService(Service):
availabilityZone=self.availabilityZone.value, availabilityZone=self.availabilityZone.value,
flavorId=self.flavor.value, flavorId=self.flavor.value,
networkId=self.network.value, networkId=self.network.value,
securityGroupsIdsList=self.securityGroups.value securityGroupsIdsList=self.securityGroups.value,
)['id'] )['id']
def removeTemplate(self, templateId: str) -> None: def removeTemplate(self, templateId: str) -> None:
@ -286,7 +342,12 @@ class LiveService(Service):
""" """
server = self.api.getServer(machineId) server = self.api.getServer(machineId)
if server['status'] in ('ERROR', 'DELETED'): if server['status'] in ('ERROR', 'DELETED'):
logger.warning('Got server status %s for %s: %s', server['status'], machineId, server.get('fault')) logger.warning(
'Got server status %s for %s: %s',
server['status'],
machineId,
server.get('fault'),
)
return server['status'] return server['status']
def startMachine(self, machineId: str) -> None: def startMachine(self, machineId: str) -> None:
@ -362,7 +423,9 @@ class LiveService(Service):
Gets the mac address of first nic of the machine Gets the mac address of first nic of the machine
""" """
net = self.api.getServer(machineId)['addresses'] net = self.api.getServer(machineId)['addresses']
vals = next(iter(net.values()))[0] # Returns "any" mac address of any interface. We just need only one interface info vals = next(iter(net.values()))[
0
] # Returns "any" mac address of any interface. We just need only one interface info
# vals = six.next(six.itervalues(net))[0] # vals = six.next(six.itervalues(net))[0]
return vals['OS-EXT-IPS-MAC:mac_addr'].upper(), vals['addr'] return vals['OS-EXT-IPS-MAC:mac_addr'].upper(), vals['addr']

View File

@ -81,7 +81,9 @@ class IPMachineDeployed(services.UserDeployment, AutoAttributes):
res = dns.resolver.resolve(ip) res = dns.resolver.resolve(ip)
ip = res[0].address ip = res[0].address
except Exception: except Exception:
self.service().parent().doLog(log.WARN, f'User service could not resolve Name {ip}.') self.service().parent().doLog(
log.WARN, f'User service could not resolve Name {ip}.'
)
return ip return ip

View File

@ -148,11 +148,7 @@ class PhysicalMachinesProvider(services.ServiceProvider):
config.read_string(self.config.value) config.read_string(self.config.value)
for key in config['wol']: for key in config['wol']:
if net.ipInNetwork(ip, key): if net.ipInNetwork(ip, key):
return ( return config['wol'][key].replace('{MAC}', mac).replace('{IP}', ip)
config['wol'][key]
.replace('{MAC}', mac)
.replace('{IP}', ip)
)
except Exception as e: except Exception as e:
logger.error('Error parsing advanced configuration: %s', e) logger.error('Error parsing advanced configuration: %s', e)

View File

@ -16,7 +16,7 @@
# may be used to endorse or promote products derived from this software # may be used to endorse or promote products derived from this software
# without specific prior written permission. # without specific prior written permission.
# #
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"u
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
@ -73,10 +73,7 @@ class IPServiceBase(services.Service):
def wakeup(self, ip: str, mac: typing.Optional[str]) -> None: def wakeup(self, ip: str, mac: typing.Optional[str]) -> None:
if mac: if mac:
wolurl = ( wolurl = self.parent().wolURL(ip, mac)
self.parent()
.wolURL(ip, mac)
)
if wolurl: if wolurl:
logger.info('Launching WOL: %s', wolurl) logger.info('Launching WOL: %s', wolurl)
try: try:

View File

@ -51,7 +51,13 @@ logger = logging.getLogger(__name__)
class IPSingleMachineService(IPServiceBase): class IPSingleMachineService(IPServiceBase):
# Gui # Gui
ip = gui.TextField(length=64, label=_('Machine IP'), order=1, tooltip=_('Machine IP'), required=True) ip = gui.TextField(
length=64,
label=_('Machine IP'),
order=1,
tooltip=_('Machine IP'),
required=True,
)
# Description of service # Description of service
typeName = _('Static Single IP') typeName = _('Static Single IP')
@ -60,7 +66,9 @@ class IPSingleMachineService(IPServiceBase):
iconFile = 'machine.png' iconFile = 'machine.png'
# Characteristics of service # Characteristics of service
maxDeployed = -1 # If the service provides more than 1 "provided service" (-1 = no limit, 0 = ???? (do not use it!!!), N = max number to deploy maxDeployed = (
-1
) # If the service provides more than 1 "provided service" (-1 = no limit, 0 = ???? (do not use it!!!), N = max number to deploy
usesCache = False # Cache are running machine awaiting to be assigned usesCache = False # Cache are running machine awaiting to be assigned
usesCache_L2 = False # L2 Cache are running machines in suspended state usesCache_L2 = False # L2 Cache are running machines in suspended state
needsManager = False # If the service needs a s.o. manager (managers are related to agents provided by services itselfs, i.e. virtual machines with agent) needsManager = False # If the service needs a s.o. manager (managers are related to agents provided by services itselfs, i.e. virtual machines with agent)
@ -75,7 +83,9 @@ class IPSingleMachineService(IPServiceBase):
return return
if not net.isValidHost(self.ip.value): if not net.isValidHost(self.ip.value):
raise IPServiceBase.ValidationException(gettext('Invalid server used: "{}"'.format(self.ip.value))) raise IPServiceBase.ValidationException(
gettext('Invalid server used: "{}"'.format(self.ip.value))
)
def getUnassignedMachine(self) -> typing.Optional[str]: def getUnassignedMachine(self) -> typing.Optional[str]:
ip: typing.Optional[str] = None ip: typing.Optional[str] = None

View File

@ -13,8 +13,18 @@ conversors: typing.MutableMapping[typing.Type, typing.Callable] = {
datetime.datetime: lambda x: datetime.datetime.fromtimestamp(int(x)), datetime.datetime: lambda x: datetime.datetime.fromtimestamp(int(x)),
} }
def convertFromDict(type: typing.Type[typing.Any], dictionary: typing.MutableMapping[str, typing.Any]) -> typing.Any:
return type(**{ k:conversors.get(type.__annotations__.get(k, str), lambda x: x)(dictionary.get(k, None)) for k in type._fields}) def convertFromDict(
type: typing.Type[typing.Any], dictionary: typing.MutableMapping[str, typing.Any]
) -> typing.Any:
return type(
**{
k: conversors.get(type.__annotations__.get(k, str), lambda x: x)(
dictionary.get(k, None)
)
for k in type._fields
}
)
class Cluster(typing.NamedTuple): class Cluster(typing.NamedTuple):
@ -28,6 +38,7 @@ class Cluster(typing.NamedTuple):
def fromDict(dictionary: typing.MutableMapping[str, typing.Any]) -> 'Cluster': def fromDict(dictionary: typing.MutableMapping[str, typing.Any]) -> 'Cluster':
return convertFromDict(Cluster, dictionary) return convertFromDict(Cluster, dictionary)
class Node(typing.NamedTuple): class Node(typing.NamedTuple):
name: str name: str
online: bool online: bool
@ -41,6 +52,7 @@ class Node(typing.NamedTuple):
def fromDict(dictionary: typing.MutableMapping[str, typing.Any]) -> 'Node': def fromDict(dictionary: typing.MutableMapping[str, typing.Any]) -> 'Node':
return convertFromDict(Node, dictionary) return convertFromDict(Node, dictionary)
class NodeStats(typing.NamedTuple): class NodeStats(typing.NamedTuple):
name: str name: str
status: str status: str
@ -61,7 +73,20 @@ class NodeStats(typing.NamedTuple):
@staticmethod @staticmethod
def empty(): def empty():
return NodeStats(name='', status='offline', uptime=0, disk=0, maxdisk=0, level='', id='', mem=1, maxmem=1, cpu=1, maxcpu=1) return NodeStats(
name='',
status='offline',
uptime=0,
disk=0,
maxdisk=0,
level='',
id='',
mem=1,
maxmem=1,
cpu=1,
maxcpu=1,
)
class ClusterStatus(typing.NamedTuple): class ClusterStatus(typing.NamedTuple):
cluster: typing.Optional[Cluster] cluster: typing.Optional[Cluster]
@ -80,6 +105,7 @@ class ClusterStatus(typing.NamedTuple):
return ClusterStatus(cluster=cluster, nodes=nodes) return ClusterStatus(cluster=cluster, nodes=nodes)
class UPID(typing.NamedTuple): class UPID(typing.NamedTuple):
node: str node: str
pid: int pid: int
@ -102,9 +128,10 @@ class UPID(typing.NamedTuple):
type=d[5], type=d[5],
vmid=int(d[6]), vmid=int(d[6]),
user=d[7], user=d[7],
upid=upid upid=upid,
) )
class TaskStatus(typing.NamedTuple): class TaskStatus(typing.NamedTuple):
node: str node: str
pid: int pid: int
@ -133,6 +160,7 @@ class TaskStatus(typing.NamedTuple):
def isErrored(self) -> bool: def isErrored(self) -> bool:
return self.isFinished() and not self.isCompleted() return self.isFinished() and not self.isCompleted()
class NetworkConfiguration(typing.NamedTuple): class NetworkConfiguration(typing.NamedTuple):
type: str type: str
mac: str mac: str
@ -154,7 +182,9 @@ class VMInfo(typing.NamedTuple):
template: bool template: bool
cpus: typing.Optional[int] cpus: typing.Optional[int]
lock: typing.Optional[str] # if suspended, lock == "suspended" & qmpstatus == "stopped" lock: typing.Optional[
str
] # if suspended, lock == "suspended" & qmpstatus == "stopped"
disk: typing.Optional[int] disk: typing.Optional[int]
maxdisk: typing.Optional[int] maxdisk: typing.Optional[int]
mem: typing.Optional[int] mem: typing.Optional[int]
@ -173,6 +203,7 @@ class VMInfo(typing.NamedTuple):
def fromDict(dictionary: typing.MutableMapping[str, typing.Any]) -> 'VMInfo': def fromDict(dictionary: typing.MutableMapping[str, typing.Any]) -> 'VMInfo':
return convertFromDict(VMInfo, dictionary) return convertFromDict(VMInfo, dictionary)
class VMConfiguration(typing.NamedTuple): class VMConfiguration(typing.NamedTuple):
name: str name: str
vga: str vga: str
@ -185,7 +216,9 @@ class VMConfiguration(typing.NamedTuple):
template: bool template: bool
@staticmethod @staticmethod
def fromDict(dictionary: typing.MutableMapping[str, typing.Any]) -> 'VMConfiguration': def fromDict(
dictionary: typing.MutableMapping[str, typing.Any]
) -> 'VMConfiguration':
nets: typing.List[NetworkConfiguration] = [] nets: typing.List[NetworkConfiguration] = []
for k in dictionary.keys(): for k in dictionary.keys():
if k[:3] == 'net': if k[:3] == 'net':
@ -194,11 +227,13 @@ class VMConfiguration(typing.NamedTuple):
dictionary['networks'] = nets dictionary['networks'] = nets
return convertFromDict(VMConfiguration, dictionary) return convertFromDict(VMConfiguration, dictionary)
class VmCreationResult(typing.NamedTuple): class VmCreationResult(typing.NamedTuple):
node: str node: str
vmid: int vmid: int
upid: UPID upid: UPID
class StorageInfo(typing.NamedTuple): class StorageInfo(typing.NamedTuple):
node: str node: str
storage: str storage: str
@ -212,11 +247,11 @@ class StorageInfo(typing.NamedTuple):
total: int total: int
used_fraction: float used_fraction: float
@staticmethod @staticmethod
def fromDict(dictionary: typing.MutableMapping[str, typing.Any]) -> 'StorageInfo': def fromDict(dictionary: typing.MutableMapping[str, typing.Any]) -> 'StorageInfo':
return convertFromDict(StorageInfo, dictionary) return convertFromDict(StorageInfo, dictionary)
class PoolInfo(typing.NamedTuple): class PoolInfo(typing.NamedTuple):
poolid: str poolid: str
comments: str comments: str

View File

@ -35,10 +35,12 @@ from django.utils.translation import ugettext as _
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def getStorage(parameters: typing.Any) -> typing.List[typing.Dict[str, typing.Any]]: def getStorage(parameters: typing.Any) -> typing.List[typing.Dict[str, typing.Any]]:
from .provider import ProxmoxProvider from .provider import ProxmoxProvider
from uds.core.environment import Environment from uds.core.environment import Environment
logger.debug('Parameters received by getResources Helper: %s', parameters) logger.debug('Parameters received by getResources Helper: %s', parameters)
env = Environment(parameters['ev']) env = Environment(parameters['ev'])
provider: ProxmoxProvider = ProxmoxProvider(env) provider: ProxmoxProvider = ProxmoxProvider(env)
@ -54,19 +56,27 @@ def getStorage(parameters: typing.Any) -> typing.List[typing.Dict[str, typing.An
res = [] res = []
# Get storages for that datacenter # Get storages for that datacenter
for storage in sorted(provider.listStorages(vmInfo.node), key=lambda x: int(not x.shared)): for storage in sorted(
provider.listStorages(vmInfo.node), key=lambda x: int(not x.shared)
):
if storage.type in ('lvm', 'iscsi', 'iscsidirect'): if storage.type in ('lvm', 'iscsi', 'iscsidirect'):
continue continue
space, free = storage.avail / 1024 / 1024 / 1024, (storage.avail - storage.used) / 1024 / 1024 / 1024 space, free = (
extra = _(' shared') if storage.shared else _(' (bound to {})').format(vmInfo.node) storage.avail / 1024 / 1024 / 1024,
res.append({'id': storage.storage, 'text': "%s (%4.2f GB/%4.2f GB)%s" % (storage.storage, space, free, extra)}) (storage.avail - storage.used) / 1024 / 1024 / 1024,
)
data = [ extra = (
_(' shared') if storage.shared else _(' (bound to {})').format(vmInfo.node)
)
res.append(
{ {
'name': 'datastore', 'id': storage.storage,
'values': res 'text': "%s (%4.2f GB/%4.2f GB)%s"
% (storage.storage, space, free, extra),
} }
] )
data = [{'name': 'datastore', 'values': res}]
logger.debug('return data: %s', data) logger.debug('return data: %s', data)
return data return data

View File

@ -52,6 +52,7 @@ logger = logging.getLogger(__name__)
CACHE_TIME_FOR_SERVER = 1800 CACHE_TIME_FOR_SERVER = 1800
MAX_VM_ID = 999999999 MAX_VM_ID = 999999999
class ProxmoxProvider( class ProxmoxProvider(
services.ServiceProvider services.ServiceProvider
): # pylint: disable=too-many-public-methods ): # pylint: disable=too-many-public-methods
@ -223,7 +224,14 @@ class ProxmoxProvider(
toPool: typing.Optional[str] = None, toPool: typing.Optional[str] = None,
) -> client.types.VmCreationResult: ) -> client.types.VmCreationResult:
return self.__getApi().cloneVm( return self.__getApi().cloneVm(
vmId, self.getNewVmId(), name, description, linkedClone, toNode, toStorage, toPool vmId,
self.getNewVmId(),
name,
description,
linkedClone,
toNode,
toStorage,
toPool,
) )
def startMachine(self, vmId: int) -> client.types.UPID: def startMachine(self, vmId: int) -> client.types.UPID:

View File

@ -45,6 +45,7 @@ if typing.TYPE_CHECKING:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class ProxmoxPublication(services.Publication): class ProxmoxPublication(services.Publication):
suggestedTime = 20 suggestedTime = 20
@ -74,7 +75,18 @@ class ProxmoxPublication(services.Publication):
""" """
returns data from an instance of Sample Publication serialized returns data from an instance of Sample Publication serialized
""" """
return '\t'.join(['v1', self._name, self._vm, self._task, self._state, self._operation, self._destroyAfter, self._reason]).encode('utf8') return '\t'.join(
[
'v1',
self._name,
self._vm,
self._task,
self._state,
self._operation,
self._destroyAfter,
self._reason,
]
).encode('utf8')
def unmarshal(self, data: bytes) -> None: def unmarshal(self, data: bytes) -> None:
""" """
@ -83,7 +95,15 @@ class ProxmoxPublication(services.Publication):
logger.debug('Data: %s', data) logger.debug('Data: %s', data)
vals = data.decode('utf8').split('\t') vals = data.decode('utf8').split('\t')
if vals[0] == 'v1': if vals[0] == 'v1':
self._name, self._vm, self._task, self._state, self._operation, self._destroyAfter, self._reason = vals[1:] (
self._name,
self._vm,
self._task,
self._state,
self._operation,
self._destroyAfter,
self._reason,
) = vals[1:]
def publish(self) -> str: def publish(self) -> str:
""" """
@ -91,8 +111,17 @@ class ProxmoxPublication(services.Publication):
""" """
try: try:
# First we should create a full clone, so base machine do not get fullfilled with "garbage" delta disks... # First we should create a full clone, so base machine do not get fullfilled with "garbage" delta disks...
self._name = 'UDS ' + _('Publication') + ' ' + self.dsName() + "-" + str(self.revision()) self._name = (
comments = _('UDS Publication for {0} created at {1}').format(self.dsName(), str(datetime.now()).split('.')[0]) 'UDS '
+ _('Publication')
+ ' '
+ self.dsName()
+ "-"
+ str(self.revision())
)
comments = _('UDS Publication for {0} created at {1}').format(
self.dsName(), str(datetime.now()).split('.')[0]
)
task = self.service().cloneMachine(self._name, comments) task = self.service().cloneMachine(self._name, comments)
self._vm = str(task.vmid) self._vm = str(task.vmid)
self._task = ','.join((task.upid.node, task.upid.upid)) self._task = ','.join((task.upid.node, task.upid.upid))
@ -105,7 +134,9 @@ class ProxmoxPublication(services.Publication):
self._reason = str(e) self._reason = str(e)
return State.ERROR return State.ERROR
def checkState(self) -> str: # pylint: disable = too-many-branches,too-many-return-statements def checkState(
self,
) -> str: # pylint: disable = too-many-branches,too-many-return-statements
if self._state != State.RUNNING: if self._state != State.RUNNING:
return self._state return self._state
node, upid = self._task.split(',') node, upid = self._task.split(',')
@ -129,7 +160,9 @@ class ProxmoxPublication(services.Publication):
if self._operation == 'p': # not Destroying if self._operation == 'p': # not Destroying
# Disable Protection (removal) # Disable Protection (removal)
self.service().setProtection(int(self._vm), protection=False) self.service().setProtection(int(self._vm), protection=False)
time.sleep(0.5) # Give some tome to proxmox. We have observed some concurrency issues time.sleep(
0.5
) # Give some tome to proxmox. We have observed some concurrency issues
# And add it to HA if # And add it to HA if
self.service().enableHA(int(self._vm)) self.service().enableHA(int(self._vm))
time.sleep(0.5) time.sleep(0.5)
@ -147,7 +180,9 @@ class ProxmoxPublication(services.Publication):
self._destroyAfter = '' self._destroyAfter = ''
def destroy(self) -> str: def destroy(self) -> str:
if self._state == State.RUNNING and self._destroyAfter is False: # If called destroy twice, will BREAK STOP publication if (
self._state == State.RUNNING and self._destroyAfter is False
): # If called destroy twice, will BREAK STOP publication
self._destroyAfter = 'y' self._destroyAfter = 'y'
return State.RUNNING return State.RUNNING

View File

@ -57,6 +57,7 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods
""" """
Proxmox Linked clones service. This is based on creating a template from selected vm, and then use it to Proxmox Linked clones service. This is based on creating a template from selected vm, and then use it to
""" """
# : Name to show the administrator. This string will be translated BEFORE # : Name to show the administrator. This string will be translated BEFORE
# : sending it to administration interface, so don't forget to # : sending it to administration interface, so don't forget to
# : mark it as _ (using ugettext_noop) # : mark it as _ (using ugettext_noop)
@ -112,14 +113,14 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods
tooltip=_('Pool that will contain UDS created vms'), tooltip=_('Pool that will contain UDS created vms'),
# tab=_('Machine'), # tab=_('Machine'),
# required=True, # required=True,
defvalue='' defvalue='',
) )
ha = gui.ChoiceField( ha = gui.ChoiceField(
label=_('HA'), label=_('HA'),
order=2, order=2,
tooltip=_('Select if HA is enabled and HA group for machines of this service'), tooltip=_('Select if HA is enabled and HA group for machines of this service'),
rdonly=True rdonly=True,
) )
guestShutdown = gui.CheckBoxField( guestShutdown = gui.CheckBoxField(
@ -137,11 +138,11 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods
fills={ fills={
'callbackName': 'pmFillResourcesFromMachine', 'callbackName': 'pmFillResourcesFromMachine',
'function': helpers.getStorage, 'function': helpers.getStorage,
'parameters': ['machine', 'ov', 'ev'] 'parameters': ['machine', 'ov', 'ev'],
}, },
tooltip=_('Service base machine'), tooltip=_('Service base machine'),
tab=_('Machine'), tab=_('Machine'),
required=True required=True,
) )
datastore = gui.ChoiceField( datastore = gui.ChoiceField(
@ -150,7 +151,7 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods
order=111, order=111,
tooltip=_('Storage for publications & machines.'), tooltip=_('Storage for publications & machines.'),
tab=_('Machine'), tab=_('Machine'),
required=True required=True,
) )
baseName = gui.TextField( baseName = gui.TextField(
@ -159,7 +160,7 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods
order=115, order=115,
tooltip=_('Base name for clones from this machine'), tooltip=_('Base name for clones from this machine'),
tab=_('Machine'), tab=_('Machine'),
required=True required=True,
) )
lenName = gui.NumericField( lenName = gui.NumericField(
@ -169,15 +170,19 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods
order=116, order=116,
tooltip=_('Size of numeric part for the names of these machines'), tooltip=_('Size of numeric part for the names of these machines'),
tab=_('Machine'), tab=_('Machine'),
required=True required=True,
) )
ov = gui.HiddenField(value=None) ov = gui.HiddenField(value=None)
ev = gui.HiddenField(value=None) # We need to keep the env so we can instantiate the Provider ev = gui.HiddenField(
value=None
) # We need to keep the env so we can instantiate the Provider
def initialize(self, values: 'Module.ValuesType') -> None: def initialize(self, values: 'Module.ValuesType') -> None:
if values: if values:
self.baseName.value = validators.validateHostname(self.baseName.value, 15, asPattern=True) self.baseName.value = validators.validateHostname(
self.baseName.value, 15, asPattern=True
)
# if int(self.memory.value) < 128: # if int(self.memory.value) < 128:
# raise Service.ValidationException(_('The minimum allowed memory is 128 Mb')) # raise Service.ValidationException(_('The minimum allowed memory is 128 Mb'))
@ -190,16 +195,23 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods
# This is not the same case, values is not the "value" of the field, but # This is not the same case, values is not the "value" of the field, but
# the list of values shown because this is a "ChoiceField" # the list of values shown because this is a "ChoiceField"
self.machine.setValues([gui.choiceItem(str(m.vmid), '{}\\{} ({})'.format(m.node, m.name or m.vmid, m.vmid)) for m in self.parent().listMachines() if m.name and m.name[:3] != 'UDS']) self.machine.setValues(
self.pool.setValues([gui.choiceItem('', _('None'))] + [gui.choiceItem(p.poolid, p.poolid) for p in self.parent().listPools()])
self.ha.setValues(
[ [
gui.choiceItem('', _('Enabled')), gui.choiceItem('__', _('Disabled')) gui.choiceItem(
] + str(m.vmid), '{}\\{} ({})'.format(m.node, m.name or m.vmid, m.vmid)
[ )
gui.choiceItem(group, group) for group in self.parent().listHaGroups() for m in self.parent().listMachines()
if m.name and m.name[:3] != 'UDS'
] ]
) )
self.pool.setValues(
[gui.choiceItem('', _('None'))]
+ [gui.choiceItem(p.poolid, p.poolid) for p in self.parent().listPools()]
)
self.ha.setValues(
[gui.choiceItem('', _('Enabled')), gui.choiceItem('__', _('Disabled'))]
+ [gui.choiceItem(group, group) for group in self.parent().listHaGroups()]
)
def parent(self) -> 'ProxmoxProvider': def parent(self) -> 'ProxmoxProvider':
return typing.cast('ProxmoxProvider', super().parent()) return typing.cast('ProxmoxProvider', super().parent())
@ -213,7 +225,9 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods
def makeTemplate(self, vmId: int) -> None: def makeTemplate(self, vmId: int) -> None:
self.parent().makeTemplate(vmId) self.parent().makeTemplate(vmId)
def cloneMachine(self, name: str, description: str, vmId: int = -1) -> 'client.types.VmCreationResult': def cloneMachine(
self, name: str, description: str, vmId: int = -1
) -> 'client.types.VmCreationResult':
name = self.sanitizeVmName(name) name = self.sanitizeVmName(name)
pool = self.pool.value or None pool = self.pool.value or None
if vmId == -1: # vmId == -1 if cloning for template if vmId == -1: # vmId == -1 if cloning for template
@ -223,7 +237,7 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods
description, description,
linkedClone=False, linkedClone=False,
toStorage=self.datastore.value, toStorage=self.datastore.value,
toPool=pool toPool=pool,
) )
return self.parent().cloneMachine( return self.parent().cloneMachine(
@ -232,7 +246,7 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods
description, description,
linkedClone=True, linkedClone=True,
toStorage=self.datastore.value, toStorage=self.datastore.value,
toPool=pool toPool=pool,
) )
def getMachineInfo(self, vmId: int) -> 'client.types.VMInfo': def getMachineInfo(self, vmId: int) -> 'client.types.VMInfo':
@ -276,7 +290,9 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods
return return
self.parent().disableHA(vmId) self.parent().disableHA(vmId)
def setProtection(self, vmId: int, node: typing.Optional[str] = None, protection: bool=False) -> None: def setProtection(
self, vmId: int, node: typing.Optional[str] = None, protection: bool = False
) -> None:
self.parent().setProtection(vmId, node, protection) self.parent().setProtection(vmId, node, protection)
def getBaseName(self) -> str: def getBaseName(self) -> str:
@ -291,5 +307,7 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods
def tryGracelyShutdown(self) -> bool: def tryGracelyShutdown(self) -> bool:
return self.guestShutdown.isTrue() return self.guestShutdown.isTrue()
def getConsoleConnection(self, machineId: str) -> typing.Optional[typing.MutableMapping[str, typing.Any]]: def getConsoleConnection(
self, machineId: str
) -> typing.Optional[typing.MutableMapping[str, typing.Any]]:
return self.parent().getConsoleConnection(machineId) return self.parent().getConsoleConnection(machineId)