From afcbd058d14c8a5a52950513ef79c1844882e03c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez=20Garc=C3=ADa?= Date: Sat, 14 Aug 2021 15:47:21 +0200 Subject: [PATCH] Formating & fixing type checkings --- server/src/uds/services/OVirt/client/ovirt.py | 254 ++++++++++++------ server/src/uds/services/OVirt/deployment.py | 107 ++++++-- server/src/uds/services/OVirt/helpers.py | 26 +- server/src/uds/services/OVirt/jobs.py | 14 +- server/src/uds/services/OVirt/provider.py | 161 ++++++++--- server/src/uds/services/OVirt/publication.py | 33 ++- server/src/uds/services/OVirt/service.py | 68 +++-- server/src/uds/services/OpenGnsys/__init__.py | 2 +- server/src/uds/services/OpenGnsys/jobs.py | 22 +- server/src/uds/services/OpenGnsys/service.py | 10 +- .../src/uds/services/OpenNebula/deployment.py | 64 +++-- .../src/uds/services/OpenNebula/on/storage.py | 5 +- .../uds/services/OpenNebula/on/template.py | 33 ++- .../src/uds/services/OpenNebula/on/types.py | 5 +- server/src/uds/services/OpenNebula/on/vm.py | 46 +++- .../src/uds/services/OpenNebula/provider.py | 108 ++++++-- .../uds/services/OpenNebula/publication.py | 18 +- server/src/uds/services/OpenNebula/service.py | 25 +- .../src/uds/services/OpenStack/deployment.py | 58 ++-- server/src/uds/services/OpenStack/helpers.py | 27 +- .../services/OpenStack/openstack/common.py | 48 +++- .../OpenStack/openstack/openstack_client.py | 1 - server/src/uds/services/OpenStack/provider.py | 10 +- .../uds/services/OpenStack/provider_legacy.py | 4 +- .../src/uds/services/OpenStack/publication.py | 34 ++- server/src/uds/services/OpenStack/service.py | 115 ++++++-- .../services/PhysicalMachines/deployment.py | 4 +- .../uds/services/PhysicalMachines/provider.py | 6 +- .../services/PhysicalMachines/service_base.py | 7 +- .../PhysicalMachines/service_single.py | 16 +- .../uds/services/Proxmox/client/__init__.py | 2 +- .../src/uds/services/Proxmox/client/types.py | 53 +++- server/src/uds/services/Proxmox/helpers.py | 30 ++- server/src/uds/services/Proxmox/provider.py | 10 +- .../src/uds/services/Proxmox/publication.py | 55 +++- server/src/uds/services/Proxmox/service.py | 64 +++-- 36 files changed, 1136 insertions(+), 409 deletions(-) diff --git a/server/src/uds/services/OVirt/client/ovirt.py b/server/src/uds/services/OVirt/client/ovirt.py index 8484adf6..2c770176 100644 --- a/server/src/uds/services/OVirt/client/ovirt.py +++ b/server/src/uds/services/OVirt/client/ovirt.py @@ -49,6 +49,7 @@ logger = logging.getLogger(__name__) lock = threading.Lock() + class Client: """ 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. """ + cached_api: typing.ClassVar[typing.Optional[ovirt.Connection]] = 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_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 _username: str @@ -79,7 +83,9 @@ class Client: Returns: 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: """ @@ -102,7 +108,13 @@ class Client: pass try: 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 except: @@ -111,7 +123,14 @@ class Client: Client.cached_api_key = None 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._username = username self._password = password @@ -135,7 +154,9 @@ class Client: """ 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 @@ -161,7 +182,7 @@ class Client: 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) @@ -172,7 +193,14 @@ class Client: pair = [vm.usb.enabled, vm.usb.type.value] except Exception: 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) @@ -181,7 +209,9 @@ class Client: finally: 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 @@ -208,7 +238,7 @@ class Client: 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]] = [] @@ -216,7 +246,11 @@ class Client: for cluster in clusters: 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 clKey = self.__getKey('o-cluster' + cluster.id) @@ -232,7 +266,9 @@ class Client: finally: 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 @@ -259,7 +295,7 @@ class Client: 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 @@ -272,7 +308,9 @@ class Client: finally: 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 @@ -308,26 +346,35 @@ class Client: api = self.__getApi() - datacenter_service = api.system_service().data_centers_service().service(datacenterId) - d = datacenter_service.get() + datacenter_service = ( + api.system_service().data_centers_service().service(datacenterId) + ) + d: typing.Any = datacenter_service.get() # type: ignore 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: active = dd.status.value except Exception: active = 'inactive' - storage.append({'id': dd.id, 'name': dd.name, 'type': dd.type.value, - 'available': dd.available, 'used': dd.used, - 'active': active == 'active'}) + storage.append( + { + 'id': dd.id, + 'name': dd.name, + 'type': dd.type.value, + 'available': dd.available, + 'used': dd.used, + 'active': active == 'active', + } + ) res = { 'name': d.name, 'id': d.id, 'storage_type': d.local and 'local' or 'shared', 'description': d.description, - 'storage': storage + 'storage': storage, } self._cache.put(dcKey, res, Client.CACHE_TIME_HIGH) @@ -335,7 +382,9 @@ class Client: finally: 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 @@ -366,14 +415,14 @@ class Client: 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 = { 'id': dd.id, 'name': dd.name, 'type': dd.type.value, 'available': dd.available, - 'used': dd.used + 'used': dd.used, } self._cache.put(sdKey, res, Client.CACHE_TIME_LOW) @@ -382,14 +431,14 @@ class Client: lock.release() def makeTemplate( - self, - name: str, - comments: str, - machineId: str, - clusterId: str, - storageId: str, - displayType: str - ) -> str: + self, + name: str, + comments: str, + machineId: str, + clusterId: str, + storageId: str, + displayType: str, + ) -> str: """ Publish the machine (makes a template from it so we can create COWs) and returns the template id of the creating machine @@ -404,7 +453,15 @@ class Client: Returns 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: lock.acquire(True) @@ -416,8 +473,8 @@ class Client: vms = api.system_service().vms_service().service(machineId) - cluster = api.system_service().clusters_service().service(clusterId).get() - vm = vms.get() + cluster: typing.Any = api.system_service().clusters_service().service(clusterId).get() # type: ignore + vm: typing.Any = vms.get() # type: ignore if vm is None: raise Exception('Machine not found') @@ -432,22 +489,19 @@ class Client: # dsks = [] # for dsk in vms.disk_attachments_service().list(): # dsks = None - # dsks.append(params.Disk(id=dsk.get_id(), storage_domains=sd, alias=dsk.get_alias())) - # dsks.append(dsk) + # dsks.append(params.Disk(id=dsk.get_id(), storage_domains=sd, alias=dsk.get_alias())) + # dsks.append(dsk) tvm = ovirt.types.Vm(id=vm.id) tcluster = ovirt.types.Cluster(id=cluster.id) template = ovirt.types.Template( - name=name, - vm=tvm, - cluster=tcluster, - description=comments + name=name, vm=tvm, cluster=tcluster, description=comments ) # display=display) - return api.system_service().templates_service().add(template).id + return api.system_service().templates_service().add(template).id # type: ignore finally: lock.release() @@ -469,7 +523,9 @@ class Client: api = self.__getApi() 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: return 'removed' @@ -482,16 +538,16 @@ class Client: lock.release() def deployFromTemplate( - self, - name: str, - comments: str, - templateId: str, - clusterId: str, - displayType: str, - usbType: str, - memoryMB: int, - guaranteedMB: int - ) -> str: + self, + name: str, + comments: str, + templateId: str, + clusterId: str, + displayType: str, + usbType: str, + memoryMB: int, + guaranteedMB: int, + ) -> str: """ Deploys a virtual machine on selected cluster from selected template @@ -507,8 +563,16 @@ class Client: Returns: 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', - name, templateId, clusterId, displayType, usbType, memoryMB, guaranteedMB) + logger.debug( + '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: lock.acquire(True) @@ -519,18 +583,28 @@ class Client: cluster = ovirt.types.Cluster(id=clusterId) 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) else: 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( - name=name, cluster=cluster, template=template, description=comments, - type=ovirt.types.VmType.DESKTOP, memory=memoryMB * 1024 * 1024, memory_policy=memoryPolicy, - usb=usb) # display=display, + name=name, + cluster=cluster, + 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: lock.release() @@ -546,7 +620,7 @@ class Client: 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 finally: lock.release() @@ -573,12 +647,12 @@ class Client: api = self.__getApi() 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 vm.status.value + return vm.status.value # type: ignore except Exception: # machine not found return 'unknown' @@ -601,7 +675,9 @@ class Client: 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: raise Exception('Machine not found') @@ -625,7 +701,9 @@ class Client: 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: raise Exception('Machine not found') @@ -649,7 +727,9 @@ class Client: 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: raise Exception('Machine not found') @@ -673,7 +753,9 @@ class Client: 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: raise Exception('Machine not found') @@ -692,12 +774,16 @@ class Client: 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: 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 nicService = vmService.nics_service().service(nic.id) nicService.update(nic) @@ -715,13 +801,15 @@ class Client: api = self.__getApi() 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) vms.update(vmu) finally: 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 """ @@ -729,7 +817,9 @@ class Client: lock.acquire(True) api = self.__getApi() - vmService = api.system_service().vms_service().service(machineId) + vmService: typing.Any = ( + api.system_service().vms_service().service(machineId) + ) vm = vmService.get() if vm is None: @@ -743,8 +833,17 @@ class Client: if display.certificate is not None: cert_subject = display.certificate.subject else: - for i in api.system_service().hosts_service().list(): - for k in api.system_service().hosts_service().service(i.id).nics_service().list(): + for i in typing.cast( + 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: cert_subject = i.certificate.subject break @@ -759,10 +858,7 @@ class Client: 'secure_port': display.secure_port, 'monitors': display.monitors, 'cert_subject': cert_subject, - 'ticket': { - 'value': ticket.value, - 'expiry': ticket.expiry - } + 'ticket': {'value': ticket.value, 'expiry': ticket.expiry}, } except Exception: diff --git a/server/src/uds/services/OVirt/deployment.py b/server/src/uds/services/OVirt/deployment.py index 3e0e80b2..fbf6fab9 100644 --- a/server/src/uds/services/OVirt/deployment.py +++ b/server/src/uds/services/OVirt/deployment.py @@ -11,7 +11,7 @@ # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # 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 # may be used to endorse or promote products derived from this software # without specific prior written permission. @@ -49,7 +49,18 @@ if typing.TYPE_CHECKING: 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' 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. """ + # : Recheck every six seconds by default (for task methods) suggestedTime = 6 @@ -100,15 +112,17 @@ class OVirtLinkedDeployment(services.UserDeployment): """ Does nothing right here, we will use environment storage in this sample """ - return b'\1'.join([ - b'v1', - self._name.encode('utf8'), - self._ip.encode('utf8'), - self._mac.encode('utf8'), - self._vmid.encode('utf8'), - self._reason.encode('utf8'), - pickle.dumps(self._queue, protocol=0) - ]) + return b'\1'.join( + [ + b'v1', + self._name.encode('utf8'), + self._ip.encode('utf8'), + self._mac.encode('utf8'), + self._vmid.encode('utf8'), + self._reason.encode('utf8'), + pickle.dumps(self._queue, protocol=0), + ] + ) def unmarshal(self, data: bytes) -> None: """ @@ -126,7 +140,9 @@ class OVirtLinkedDeployment(services.UserDeployment): def getName(self) -> str: if self._name == '': 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: return NO_MORE_NAMES return self._name @@ -207,7 +223,9 @@ class OVirtLinkedDeployment(services.UserDeployment): if 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) def desktopLogin(self, username: str, password: str, domain: str = '') -> None: @@ -215,7 +233,9 @@ class OVirtLinkedDeployment(services.UserDeployment): if sys.platform == 'win32': from uds import operations 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 # operations.writeToPipe("\\\\.\\pipe\\VDSMDPipe", packet, True) dbService = self.dbservice() @@ -254,8 +274,15 @@ if sys.platform == 'win32': else: self._queue = [opCreate, opChangeMac, opStart, opWait, opSuspend, opFinish] - def __checkMachineState(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) + def __checkMachineState( + 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) # 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, opWait: self.__wait, opRemove: self.__remove, - opChangeMac: self.__changeMac + opChangeMac: self.__changeMac, } try: execFnc: typing.Optional[typing.Callable[[], str]] = fncs.get(op, 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() @@ -368,9 +397,13 @@ if sys.platform == 'win32': templateId = self.publication().getTemplateId() name = self.getName() 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' self._vmid = self.service().deployFromTemplate(name, comments, templateId) @@ -409,7 +442,9 @@ if sys.platform == 'win32': return State.RUNNING 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) return State.RUNNING @@ -427,7 +462,9 @@ if sys.platform == 'win32': return State.RUNNING 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: self.service().stopMachine(self._vmid) @@ -446,7 +483,9 @@ if sys.platform == 'win32': return State.RUNNING 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: self.service().suspendMachine(self._vmid) @@ -522,14 +561,18 @@ if sys.platform == 'win32': opStop: self.__checkStop, opSuspend: self.__checkSuspend, opRemove: self.__checkRemoved, - opChangeMac: self.__checkMac + opChangeMac: self.__checkMac, } 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: - 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() if state == State.FINISHED: @@ -613,8 +656,16 @@ if sys.platform == 'win32': opError: 'error', opFinish: 'finish', opRetry: 'retry', - opChangeMac: 'changing mac' + opChangeMac: 'changing mac', }.get(op, '????') 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], + ) diff --git a/server/src/uds/services/OVirt/helpers.py b/server/src/uds/services/OVirt/helpers.py index 9f672e38..a4297788 100644 --- a/server/src/uds/services/OVirt/helpers.py +++ b/server/src/uds/services/OVirt/helpers.py @@ -13,12 +13,14 @@ if typing.TYPE_CHECKING: logger = logging.getLogger(__name__) + 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 """ from .provider import OVirtProvider from uds.core.environment import Environment + logger.debug('Parameters received by getResources Helper: %s', parameters) env = Environment(parameters['ev']) provider: 'OVirtProvider' = OVirtProvider(env) @@ -31,15 +33,23 @@ def getResources(parameters: typing.Any) -> typing.List[typing.Dict[str, typing. # Get storages for that datacenter for storage in provider.getDatacenterInfo(ci['datacenter_id'])['storage']: 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)')}) - data = [ - { - 'name': 'datastore', - 'values': res - } - ] + res.append( + { + 'id': storage['id'], + '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) return data diff --git a/server/src/uds/services/OVirt/jobs.py b/server/src/uds/services/OVirt/jobs.py index 62edcd1d..81c333e0 100644 --- a/server/src/uds/services/OVirt/jobs.py +++ b/server/src/uds/services/OVirt/jobs.py @@ -58,7 +58,9 @@ class OVirtDeferredRemoval(jobs.Job): @staticmethod 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 try: # 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: 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: logger.warning('Exception got queuing for Removal: %s', e) @@ -84,7 +90,9 @@ class OVirtDeferredRemoval(jobs.Job): provider: Provider # 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) storage = provider.getEnvironment().storage diff --git a/server/src/uds/services/OVirt/provider.py b/server/src/uds/services/OVirt/provider.py index 10f64656..ed8a6b84 100644 --- a/server/src/uds/services/OVirt/provider.py +++ b/server/src/uds/services/OVirt/provider.py @@ -52,7 +52,9 @@ logger = logging.getLogger(__name__) 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 @@ -69,6 +71,7 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi we MUST register it at package __init__. """ + # : What kind of services we offer, this are classes inherited from Service offers = [OVirtLinkedService] # : 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=[ 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) - 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) + host = gui.TextField( + length=64, + 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) - 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) + 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, + ) + 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) - 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) + 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, + ) + 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 _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) """ 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 @@ -164,7 +228,9 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi """ 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. 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) - 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. @@ -200,7 +268,9 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi 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 @@ -218,7 +288,9 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi """ 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 @@ -246,7 +318,9 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi """ 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 @@ -269,14 +343,14 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi return self.__getApi().getStorageInfo(storageId, force) def makeTemplate( - self, - name: str, - comments: str, - machineId: str, - clusterId: str, - storageId: str, - displayType: str - ) -> str: + self, + name: str, + comments: str, + machineId: str, + clusterId: str, + storageId: str, + displayType: str, + ) -> str: """ Publish the machine (makes a template from it so we can create COWs) and returns the template id of the creating machine @@ -291,7 +365,9 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi Returns 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: """ @@ -333,16 +409,16 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi return self.__getApi().removeTemplate(templateId) def deployFromTemplate( - self, - name: str, - comments: str, - templateId: str, - clusterId: str, - displayType: str, - usbType: str, - memoryMB: int, - guaranteedMB: int - ) -> str: + self, + name: str, + comments: str, + templateId: str, + clusterId: str, + displayType: str, + usbType: str, + memoryMB: int, + guaranteedMB: int, + ) -> str: """ Deploys a virtual machine on selected cluster from selected template @@ -358,7 +434,16 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi Returns: 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: """ @@ -418,7 +503,9 @@ class OVirtProvider(services.ServiceProvider): # pylint: disable=too-many-publi def getMacRange(self) -> str: 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) @staticmethod diff --git a/server/src/uds/services/OVirt/publication.py b/server/src/uds/services/OVirt/publication.py index 62a3d0b8..f8f41e13 100644 --- a/server/src/uds/services/OVirt/publication.py +++ b/server/src/uds/services/OVirt/publication.py @@ -50,7 +50,9 @@ class OVirtPublication(Publication): 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 _reason: str _destroyAfter: str @@ -80,7 +82,16 @@ class OVirtPublication(Publication): """ 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: """ @@ -89,14 +100,24 @@ class OVirtPublication(Publication): logger.debug('Data: %s', data) vals = data.decode('utf8').split('\t') 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: """ Realizes the publication of the service """ - self._name = self.service().sanitizeVmName('UDSP ' + self.dsName() + "-" + str(self.revision())) - comments = _('UDS pub for {0} at {1}').format(self.dsName(), str(datetime.now()).split('.')[0]) + self._name = self.service().sanitizeVmName( + '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._destroyAfter = 'f' self._state = 'locked' @@ -123,7 +144,7 @@ class OVirtPublication(Publication): try: self._state = self.service().getTemplateState(self._templateId) if self._state == 'removed': - raise Exception('Template has been removed!') + raise Exception('Template has been removed!') except Exception as e: self._state = 'error' self._reason = str(e) diff --git a/server/src/uds/services/OVirt/service.py b/server/src/uds/services/OVirt/service.py index 4195ae34..8b319d3a 100644 --- a/server/src/uds/services/OVirt/service.py +++ b/server/src/uds/services/OVirt/service.py @@ -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 """ + # : Name to show the administrator. This string will be translated BEFORE # : sending it to administration interface, so don't forget to # : mark it as _ (using ugettext_noop) @@ -113,9 +114,10 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods fills={ 'callbackName': 'ovFillResourcesFromCluster', '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( @@ -123,7 +125,7 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods rdonly=False, order=101, tooltip=_('Datastore domain where to publish and put incrementals'), - required=True + required=True, ) minSpaceGB = gui.NumericField( @@ -133,7 +135,7 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods minValue=0, order=102, tooltip=_('Minimal free space in GB'), - required=True + required=True, ) machine = gui.ChoiceField( @@ -141,7 +143,7 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods order=110, tooltip=_('Service base machine'), tab=_('Machine'), - required=True + required=True, ) memory = gui.NumericField( @@ -153,7 +155,7 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods order=111, tooltip=_('Memory assigned to machines'), tab=_('Machine'), - required=True + required=True, ) memoryGuaranteed = gui.NumericField( @@ -165,7 +167,7 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods order=112, tooltip=_('Physical memory guaranteed to machines'), tab=_('Machine'), - required=True + required=True, ) usb = gui.ChoiceField( @@ -179,7 +181,7 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods # gui.choiceItem('legacy', 'legacy (deprecated)'), ], 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( @@ -187,12 +189,9 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods rdonly=False, order=114, tooltip=_('Display type (only for administration purposes)'), - values=[ - gui.choiceItem('spice', 'Spice'), - gui.choiceItem('vnc', 'Vnc') - ], + values=[gui.choiceItem('spice', 'Spice'), gui.choiceItem('vnc', 'Vnc')], 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( label=_('Machine Names'), @@ -200,7 +199,7 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods order=115, tooltip=_('Base name for clones from this machine'), tab=_('Machine'), - required=True + required=True, ) lenName = gui.NumericField( @@ -210,11 +209,13 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods order=116, tooltip=_('Size of numeric part for the names of these machines'), tab=_('Machine'), - required=True + required=True, ) 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: """ @@ -226,7 +227,9 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods if values: tools.checkValidBasename(self.baseName.value, self.lenName.num()) 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): 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) availableGB = info['available'] / (1024 * 1024 * 1024) 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: """ @@ -301,7 +308,14 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods # Get storages for that datacenter 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: """ @@ -333,8 +347,16 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods """ logger.debug('Deploying from template %s machine %s', templateId, name) self.datastoreHasSpace() - return self.parent().deployFromTemplate(name, comments, templateId, self.cluster.value, - self.display.value, self.usb.value, int(self.memory.value), int(self.memoryGuaranteed.value)) + return self.parent().deployFromTemplate( + 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: """ @@ -440,5 +462,7 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods """ 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) diff --git a/server/src/uds/services/OpenGnsys/__init__.py b/server/src/uds/services/OpenGnsys/__init__.py index a5543de0..86d928b2 100644 --- a/server/src/uds/services/OpenGnsys/__init__.py +++ b/server/src/uds/services/OpenGnsys/__init__.py @@ -32,5 +32,5 @@ from .provider import OGProvider from .jobs import OpenGnsysMaintainer # Scheduled task to do clean processes -for cls in (OpenGnsysMaintainer, ): +for cls in (OpenGnsysMaintainer,): managers.taskManager().registerJob(cls) diff --git a/server/src/uds/services/OpenGnsys/jobs.py b/server/src/uds/services/OpenGnsys/jobs.py index b65baded..cbcbf133 100644 --- a/server/src/uds/services/OpenGnsys/jobs.py +++ b/server/src/uds/services/OpenGnsys/jobs.py @@ -48,8 +48,9 @@ if typing.TYPE_CHECKING: logger = logging.getLogger(__name__) + 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' def run(self) -> None: @@ -57,18 +58,29 @@ class OpenGnsysMaintainer(jobs.Job): # Look for Providers of type VMWareVCServiceProvider 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) # Locate all services inside the provider service: models.Service for service in provider.services.all(): 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 userService: models.UserService - for userService in models.UserService.objects.filter(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) + for userService in models.UserService.objects.filter( + 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() logger.debug('OpenGnsys job finished') diff --git a/server/src/uds/services/OpenGnsys/service.py b/server/src/uds/services/OpenGnsys/service.py index 9de48b89..7b476e76 100644 --- a/server/src/uds/services/OpenGnsys/service.py +++ b/server/src/uds/services/OpenGnsys/service.py @@ -140,7 +140,9 @@ class OGService(Service): label=_('Start if unavailable'), defvalue=gui.TRUE, 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) @@ -180,7 +182,7 @@ class OGService(Service): machineId, self.getLoginNotifyURL(uuid, token), self.getLogoutNotifyURL(uuid, token), - self.getReleaseURL(uuid, token) + self.getReleaseURL(uuid, token), ) def notifyDeadline( @@ -191,13 +193,13 @@ class OGService(Service): def powerOn(self, machineId: str) -> typing.Any: return self.parent().powerOn(machineId, self.image.value) - def _notifyURL(self, uuid: str, token:str, message: str) -> str: + def _notifyURL(self, uuid: str, token: str, message: str) -> str: # The URL is "GET messages URL". return '{accessURL}uds/ognotify/{message}/{token}/{uuid}'.format( accessURL=self.parent().getUDSServerAccessUrl(), uuid=uuid, token=token, - message=message + message=message, ) def getLoginNotifyURL(self, uuid: str, token: str) -> str: diff --git a/server/src/uds/services/OpenNebula/deployment.py b/server/src/uds/services/OpenNebula/deployment.py index 74d26e89..ca7e76cc 100644 --- a/server/src/uds/services/OpenNebula/deployment.py +++ b/server/src/uds/services/OpenNebula/deployment.py @@ -53,6 +53,7 @@ opCreate, opStart, opShutdown, opRemove, opWait, opError, opFinish, opRetry = ra NO_MORE_NAMES = 'NO-NAME-ERROR' + class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods # : Recheck every six seconds by default (for task methods) suggestedTime = 6 @@ -65,7 +66,6 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods _reason: str = '' _queue: typing.List[int] - def initialize(self): self._name = '' self._ip = '' @@ -85,15 +85,17 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods # Serializable needed methods def marshal(self) -> bytes: - return b'\1'.join([ - b'v1', - self._name.encode('utf8'), - self._ip.encode('utf8'), - self._mac.encode('utf8'), - self._vmid.encode('utf8'), - self._reason.encode('utf8'), - pickle.dumps(self._queue, protocol=0) - ]) + return b'\1'.join( + [ + b'v1', + self._name.encode('utf8'), + self._ip.encode('utf8'), + self._mac.encode('utf8'), + self._vmid.encode('utf8'), + self._reason.encode('utf8'), + pickle.dumps(self._queue, protocol=0), + ] + ) def unmarshal(self, data: bytes) -> None: vals = data.split(b'\1') @@ -108,7 +110,9 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods def getName(self) -> str: if self._name == '': 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: return NO_MORE_NAMES return self._name @@ -179,11 +183,19 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods self._queue = [opCreate, opStart, opWait, opShutdown, opFinish] 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) # 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') 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) 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() @@ -293,9 +307,13 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods templateId = self.publication().getTemplateId() name = self.getName() 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) 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) 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() if state == State.FINISHED: @@ -476,4 +496,12 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods }.get(op, '????') 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], + ) diff --git a/server/src/uds/services/OpenNebula/on/storage.py b/server/src/uds/services/OpenNebula/on/storage.py index d5b2557f..725c3676 100644 --- a/server/src/uds/services/OpenNebula/on/storage.py +++ b/server/src/uds/services/OpenNebula/on/storage.py @@ -39,7 +39,10 @@ if typing.TYPE_CHECKING: 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 """ diff --git a/server/src/uds/services/OpenNebula/on/template.py b/server/src/uds/services/OpenNebula/on/template.py index d3166d86..aa223da8 100644 --- a/server/src/uds/services/OpenNebula/on/template.py +++ b/server/src/uds/services/OpenNebula/on/template.py @@ -45,13 +45,18 @@ if typing.TYPE_CHECKING: 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(): if t.name[:4] != 'UDSP': 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 the creating machine @@ -92,7 +97,11 @@ def create(api: 'client.OpenNebulaClient', fromTemplateId: str, name: str, toDat try: imgId = imgs[imgName.strip()] 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: fromId = True node = imgIds[0].childNodes[0] @@ -105,7 +114,9 @@ def create(api: 'client.OpenNebulaClient', fromTemplateId: str, name: str, toDat # Now clone the image 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 if fromId is True: node.data = str(newId) @@ -120,7 +131,9 @@ def create(api: 'client.OpenNebulaClient', fromTemplateId: str, name: str, toDat except Exception as e: logger.exception('Creating template on OpenNebula') 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: pass raise e @@ -165,6 +178,7 @@ def remove(api: 'client.OpenNebulaClient', templateId: str) -> None: except Exception: logger.error('Removing template on OpenNebula') + def deployFrom(api: 'client.OpenNebulaClient', templateId: str, name: str) -> str: """ 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: 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 + def checkPublished(api: 'client.OpenNebulaClient', templateId): """ 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): return False 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 api.makePersistentImage(imgId, False) diff --git a/server/src/uds/services/OpenNebula/on/types.py b/server/src/uds/services/OpenNebula/on/types.py index 879f0a6a..bd9996c2 100644 --- a/server/src/uds/services/OpenNebula/on/types.py +++ b/server/src/uds/services/OpenNebula/on/types.py @@ -32,6 +32,7 @@ import enum import typing + class VmState(enum.Enum): # pylint: disable=too-few-public-methods INIT = 0 PENDING = 1 @@ -81,7 +82,7 @@ class StorageType(typing.NamedTuple): id: str name: str total: int # In Megabytes - free: int # In Megabytes + free: int # In Megabytes xml: typing.Optional[str] @@ -95,7 +96,7 @@ class TemplateType(typing.NamedTuple): class ImageType(typing.NamedTuple): id: str name: str - size: int # In Megabytes + size: int # In Megabytes persistent: bool running_vms: int state: ImageState diff --git a/server/src/uds/services/OpenNebula/on/vm.py b/server/src/uds/services/OpenNebula/on/vm.py index d195d744..b4eeadc8 100644 --- a/server/src/uds/services/OpenNebula/on/vm.py +++ b/server/src/uds/services/OpenNebula/on/vm.py @@ -58,7 +58,9 @@ def getMachineState(api: 'client.OpenNebulaClient', machineId: str) -> types.VmS try: return api.getVMState(machineId) 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 @@ -70,7 +72,9 @@ def getMachineSubstate(api: 'client.OpenNebulaClient', machineId: str) -> int: try: return api.getVMSubstate(machineId) 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 @@ -122,6 +126,7 @@ def suspendMachine(api: 'client.OpenNebulaClient', machineId: str) -> None: except Exception as e: logger.error('Error suspending %s on OpenNebula: %s', machineId, e) + def shutdownMachine(api: 'client.OpenNebulaClient', machineId: str) -> None: ''' 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) -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. Machines starting with UDS are filtered out @@ -189,7 +196,11 @@ def enumerateMachines(api: 'client.OpenNebulaClient') -> typing.Iterable[types.V 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 ''' @@ -204,7 +215,9 @@ def getNetInfo(api: 'client.OpenNebulaClient', machineId: str, networkId: typing node = nic break 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()) @@ -217,10 +230,14 @@ def getNetInfo(api: 'client.OpenNebulaClient', machineId: str, networkId: typing return (node.getElementsByTagName('MAC')[0].childNodes[0].data, ip) 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 SPICE connections should check that 'type' is 'SPICE' @@ -236,17 +253,18 @@ def getDisplayConnection(api: 'client.OpenNebulaClient', machineId: str) -> typi except Exception: passwd = '' - host = md.getElementsByTagName('HISTORY_RECORDS')[0].lastChild.getElementsByTagName('HOSTNAME')[0].childNodes[0].data - return { - 'type': type_, - 'host': host, - 'port': int(port), - 'passwd': passwd - } + host = ( + md.getElementsByTagName('HISTORY_RECORDS')[0] + .lastChild.getElementsByTagName('HOSTNAME')[0] + .childNodes[0] + .data + ) + return {'type': type_, 'host': host, 'port': int(port), 'passwd': passwd} except Exception: return None # No SPICE connection + # Sample NIC Content (there will be as much as nics) # # diff --git a/server/src/uds/services/OpenNebula/provider.py b/server/src/uds/services/OpenNebula/provider.py index c65658c0..a9677c12 100644 --- a/server/src/uds/services/OpenNebula/provider.py +++ b/server/src/uds/services/OpenNebula/provider.py @@ -74,16 +74,72 @@ class OpenNebulaProvider(ServiceProvider): # pylint: disable=too-many-public-me # but used for sample purposes # If we don't indicate an order, the output order of fields will be # "random" - host = gui.TextField(length=64, label=_('Host'), order=1, tooltip=_('OpenNebula Host'), required=True) - port = gui.NumericField(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) + host = gui.TextField( + length=64, label=_('Host'), order=1, tooltip=_('OpenNebula Host'), required=True + ) + port = gui.NumericField( + 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) - 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) + 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, + ) + 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 _api: typing.Optional[on.client.OpenNebulaClient] = None @@ -102,12 +158,16 @@ class OpenNebulaProvider(ServiceProvider): # pylint: disable=too-many-public-me @property 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 def api(self) -> on.client.OpenNebulaClient: 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 @@ -128,16 +188,23 @@ class OpenNebulaProvider(ServiceProvider): # pylint: disable=too-many-public-me try: 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: return [False, '{}'.format(e)] 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) - 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) 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) - 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 ''' @@ -250,20 +319,17 @@ class OpenNebulaProvider(ServiceProvider): # pylint: disable=too-many-public-me 'type': display['type'], 'address': display['host'], 'port': display['port'], - 'secure_port':-1, + 'secure_port': -1, 'monitors': 1, 'cert_subject': '', - 'ticket': { - 'value': display['passwd'], - 'expiry': '' - } + 'ticket': {'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 ''' - return + return dict() @staticmethod def test(env: 'Environment', data: 'Module.ValuesType') -> typing.List[typing.Any]: diff --git a/server/src/uds/services/OpenNebula/publication.py b/server/src/uds/services/OpenNebula/publication.py index 65aa78ea..8c55d4a9 100644 --- a/server/src/uds/services/OpenNebula/publication.py +++ b/server/src/uds/services/OpenNebula/publication.py @@ -38,7 +38,7 @@ from uds.core.util.state import State # Not imported at runtime, just for type checking if typing.TYPE_CHECKING: - from . import service + from .service import LiveService logger = logging.getLogger(__name__) @@ -48,21 +48,25 @@ class LivePublication(Publication): 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 = '' _reason: str = '' _templateId: str = '' _state: str = 'r' - def service(self) -> 'service.LiveService': - return typing.cast('service.LiveService', super().service()) + def service(self) -> 'LiveService': + return typing.cast('LiveService', super().service()) def marshal(self) -> bytes: """ 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: """ @@ -77,7 +81,9 @@ class LivePublication(Publication): """ 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._state = 'running' diff --git a/server/src/uds/services/OpenNebula/service.py b/server/src/uds/services/OpenNebula/service.py index 0943cc20..491aa51c 100644 --- a/server/src/uds/services/OpenNebula/service.py +++ b/server/src/uds/services/OpenNebula/service.py @@ -55,6 +55,7 @@ class LiveService(Service): """ Opennebula Live Service """ + # : Name to show the administrator. This string will be translated BEFORE # : sending it to administration interface, so don't forget to # : mark it as _ (using ugettext_noop) @@ -109,7 +110,7 @@ class LiveService(Service): label=_("Datastore"), order=100, tooltip=_('Service clones datastore'), - required=True + required=True, ) template = gui.ChoiceField( @@ -117,7 +118,7 @@ class LiveService(Service): order=110, tooltip=_('Service base template'), tab=_('Machine'), - required=True + required=True, ) baseName = gui.TextField( @@ -126,7 +127,7 @@ class LiveService(Service): order=111, tooltip=_('Base name for clones from this machine'), tab=_('Machine'), - required=True + required=True, ) lenName = gui.NumericField( @@ -136,7 +137,7 @@ class LiveService(Service): order=112, tooltip=_('Size of numeric part for the names of these machines'), tab=_('Machine'), - required=True + required=True, ) def initialize(self, values: 'Module.ValuesType') -> None: @@ -149,7 +150,9 @@ class LiveService(Service): if not values: 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': return typing.cast('OpenNebulaProvider', super().parent()) @@ -173,7 +176,9 @@ class LiveService(Service): return self.parent().sanitizeVmName(name) 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: return self.parent().checkTemplatePublished(templateId) @@ -288,7 +293,9 @@ class LiveService(Service): """ 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 """ @@ -309,5 +316,7 @@ class LiveService(Service): def getConsoleConnection(self, machineId: str) -> typing.Dict[str, typing.Any]: 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) diff --git a/server/src/uds/services/OpenStack/deployment.py b/server/src/uds/services/OpenStack/deployment.py index 1f1e37aa..4c2fdf73 100644 --- a/server/src/uds/services/OpenStack/deployment.py +++ b/server/src/uds/services/OpenStack/deployment.py @@ -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. """ + _name: str = '' _ip: str = '' _mac: str = '' @@ -92,15 +93,17 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods # Serializable needed methods def marshal(self) -> bytes: - return b'\1'.join([ - b'v1', - self._name.encode('utf8'), - self._ip.encode('utf8'), - self._mac.encode('utf8'), - self._vmid.encode('utf8'), - self._reason.encode('utf8'), - pickle.dumps(self._queue, protocol=0) - ]) + return b'\1'.join( + [ + b'v1', + self._name.encode('utf8'), + self._ip.encode('utf8'), + self._mac.encode('utf8'), + self._vmid.encode('utf8'), + self._reason.encode('utf8'), + pickle.dumps(self._queue, protocol=0), + ] + ) def unmarshal(self, data: bytes) -> None: vals = data.split(b'\1') @@ -115,7 +118,9 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods def getName(self) -> str: if self._name == '': 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: return NO_MORE_NAMES return self._name @@ -188,7 +193,12 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods self._queue = [opCreate, opWait, opSuspend, opFinish] 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) # 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: 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]() @@ -295,9 +307,13 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods templateId = self.publication().getTemplateId() name = self.getName() 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) if self._vmid is None: @@ -388,7 +404,9 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods try: 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]() if state == State.FINISHED: @@ -462,4 +480,12 @@ class LiveDeployment(UserDeployment): # pylint: disable=too-many-public-methods }.get(op, '????') 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], + ) diff --git a/server/src/uds/services/OpenStack/helpers.py b/server/src/uds/services/OpenStack/helpers.py index 316b7713..cdb1d93f 100644 --- a/server/src/uds/services/OpenStack/helpers.py +++ b/server/src/uds/services/OpenStack/helpers.py @@ -39,6 +39,7 @@ from . import openstack logger = logging.getLogger(__name__) + def getApi(parameters: typing.Dict[str, str]) -> typing.Tuple[openstack.Client, bool]: from .provider_legacy import ProviderLegacy 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) -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 ''' api, nameFromSubnets = getApi(parameters) 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()] - securityGroups = [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()] + securityGroups = [ + 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 = [ {'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) 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 ''' api, _ = getApi(parameters) # 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'] != ''] + volumes = [ + gui.choiceItem(v['id'], v['name']) for v in api.listVolumes() if v['name'] != '' + ] data = [ {'name': 'volume', 'values': volumes}, diff --git a/server/src/uds/services/OpenStack/openstack/common.py b/server/src/uds/services/OpenStack/openstack/common.py index f529ddc3..2a2c346a 100644 --- a/server/src/uds/services/OpenStack/openstack/common.py +++ b/server/src/uds/services/OpenStack/openstack/common.py @@ -38,17 +38,45 @@ import logging logger = logging.getLogger(__name__) ( - ACTIVE, BUILDING, DELETED, ERROR, - HARD_REBOOT, MIGRATING, PASSWORD, - PAUSED, REBOOT, REBUILD, RESCUED, - RESIZED, REVERT_RESIZE, SOFT_DELETED, - STOPPED, SUSPENDED, UNKNOWN, VERIFY_RESIZE, SHUTOFF + ACTIVE, + BUILDING, + DELETED, + ERROR, + HARD_REBOOT, + MIGRATING, + PASSWORD, + PAUSED, + REBOOT, + REBUILD, + RESCUED, + RESIZED, + REVERT_RESIZE, + SOFT_DELETED, + STOPPED, + SUSPENDED, + UNKNOWN, + VERIFY_RESIZE, + SHUTOFF, ) = ( - 'ACTIVE', 'BUILDING', 'DELETED', 'ERROR', - 'HARD_REBOOT', 'MIGRATING', 'PASSWORD', - 'PAUSED', 'REBOOT', 'REBUILD', 'RESCUED', - 'RESIZED', 'REVERT_RESIZE', 'SOFT_DELETED', - 'STOPPED', 'SUSPENDED', 'UNKNOWN', 'VERIFY_RESIZE', 'SHUTOFF' + 'ACTIVE', + 'BUILDING', + 'DELETED', + 'ERROR', + 'HARD_REBOOT', + 'MIGRATING', + 'PASSWORD', + 'PAUSED', + 'REBOOT', + 'REBUILD', + 'RESCUED', + 'RESIZED', + 'REVERT_RESIZE', + 'SOFT_DELETED', + 'STOPPED', + 'SUSPENDED', + 'UNKNOWN', + 'VERIFY_RESIZE', + 'SHUTOFF', ) diff --git a/server/src/uds/services/OpenStack/openstack/openstack_client.py b/server/src/uds/services/OpenStack/openstack/openstack_client.py index 4176bfe9..57c781d3 100644 --- a/server/src/uds/services/OpenStack/openstack/openstack_client.py +++ b/server/src/uds/services/OpenStack/openstack/openstack_client.py @@ -281,7 +281,6 @@ class Client: # pylint: disable=too-many-public-methods else: self._volume = 'volumev2' - def ensureAuthenticated(self) -> None: if ( self._authenticated is False diff --git a/server/src/uds/services/OpenStack/provider.py b/server/src/uds/services/OpenStack/provider.py index 28fc8370..630546f5 100644 --- a/server/src/uds/services/OpenStack/provider.py +++ b/server/src/uds/services/OpenStack/provider.py @@ -208,7 +208,9 @@ class OpenStackProvider(ServiceProvider): length=96, label=_('Proxy'), 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, tab=gui.ADVANCED_TAB, ) @@ -233,9 +235,7 @@ class OpenStackProvider(ServiceProvider): if self._api is None: proxies = None if self.httpsProxy.value.strip(): - proxies = { - 'https': self.httpsProxy.value - } + proxies = {'https': self.httpsProxy.value} self._api = openstack.Client( self.endpoint.value, -1, @@ -247,7 +247,7 @@ class OpenStackProvider(ServiceProvider): projectId=projectId, region=region, access=self.access.value, - proxies=proxies + proxies=proxies, ) return self._api diff --git a/server/src/uds/services/OpenStack/provider_legacy.py b/server/src/uds/services/OpenStack/provider_legacy.py index 402fbd25..1cb8c2bb 100644 --- a/server/src/uds/services/OpenStack/provider_legacy.py +++ b/server/src/uds/services/OpenStack/provider_legacy.py @@ -194,7 +194,9 @@ class ProviderLegacy(ServiceProvider): length=96, label=_('Proxy'), 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, tab=gui.ADVANCED_TAB, ) diff --git a/server/src/uds/services/OpenStack/publication.py b/server/src/uds/services/OpenStack/publication.py index 8b579d22..2a82dfcd 100644 --- a/server/src/uds/services/OpenStack/publication.py +++ b/server/src/uds/services/OpenStack/publication.py @@ -47,13 +47,16 @@ class LivePublication(Publication): """ This class provides the publication of a oVirtLinkedService """ + _name: str = '' _reason: str = '' _templateId: str = '' _state: str = 'r' _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): """ @@ -78,7 +81,16 @@ class LivePublication(Publication): """ 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: """ @@ -86,13 +98,21 @@ class LivePublication(Publication): """ vals = data.decode('utf8').split('\t') 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: """ 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._destroyAfter = 'n' @@ -118,7 +138,9 @@ class LivePublication(Publication): if self._state == 'available': 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': return self.destroy() @@ -131,7 +153,7 @@ class LivePublication(Publication): def destroy(self) -> str: # We do not do anything else to destroy this instance of publication if self._state == 'error': - return State.ERROR # Nothing to cancel + return State.ERROR # Nothing to cancel if self._state == 'creating': self._destroyAfter = 'y' diff --git a/server/src/uds/services/OpenStack/service.py b/server/src/uds/services/OpenStack/service.py index 22c0ceef..377b2533 100644 --- a/server/src/uds/services/OpenStack/service.py +++ b/server/src/uds/services/OpenStack/service.py @@ -51,6 +51,7 @@ if typing.TYPE_CHECKING: from . import openstack from .provider import OpenStackProvider from .provider_legacy import ProviderLegacy + Provider = typing.Union[OpenStackProvider, ProviderLegacy] @@ -58,6 +59,7 @@ class LiveService(Service): """ OpenStack Live Service """ + # : Name to show the administrator. This string will be translated BEFORE # : sending it to administration interface, so don't forget to # : mark it as _ (using ugettext_noop) @@ -104,37 +106,74 @@ class LiveService(Service): servicesTypeProvided = (serviceTypes.VDI,) # 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( label=_('Project'), order=2, fills={ - 'callbackName' : 'osFillResources', - 'function' : helpers.getResources, - 'parameters' : ['ov', 'ev', 'project', 'region', 'legacy'] + 'callbackName': 'osFillResources', + 'function': helpers.getResources, + 'parameters': ['ov', 'ev', 'project', 'region', 'legacy'], }, tooltip=_('Project for this service'), required=True, - rdonly=True + rdonly=True, ) availabilityZone = gui.ChoiceField( label=_('Availability Zones'), order=3, fills={ - 'callbackName' : 'osFillVolumees', - 'function' : helpers.getVolumes, - 'parameters' : ['ov', 'ev', 'project', 'region', 'availabilityZone', 'legacy'] + 'callbackName': 'osFillVolumees', + 'function': helpers.getVolumes, + 'parameters': [ + 'ov', + 'ev', + 'project', + 'region', + 'availabilityZone', + 'legacy', + ], }, tooltip=_('Service availability zones'), 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) - network = gui.ChoiceField(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')) + network = gui.ChoiceField( + 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( label=_('Machine Names'), @@ -142,7 +181,7 @@ class LiveService(Service): order=9, tooltip=_('Base name for clones from this machine'), required=True, - tab=_('Machine') + tab=_('Machine'), ) lenName = gui.NumericField( @@ -152,12 +191,14 @@ class LiveService(Service): order=10, tooltip=_('Size of numeric part for the names of these machines'), required=True, - tab=_('Machine') + tab=_('Machine'), ) ov = 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 @@ -183,15 +224,26 @@ class LiveService(Service): """ api = self.parent().api() - if not self.parent().legacy and self.parent().region.value: - regions = [gui.choiceItem(self.parent().region.value, self.parent().region.value)] + # Checks if legacy or current openstack provider + 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: regions = [gui.choiceItem(r['id'], r['id']) for r in api.listRegions()] self.region.setValues(regions) - if not self.parent().legacy and self.parent().tenant.value: - tenants = [gui.choiceItem(self.parent().tenant.value, self.parent().tenant.value)] + if parentCurrent and parentCurrent.tenant.value: + tenants = [ + gui.choiceItem(parentCurrent.tenant.value, parentCurrent.tenant.value) + ] else: tenants = [gui.choiceItem(t['id'], t['name']) for t in api.listProjects()] self.project.setValues(tenants) @@ -206,7 +258,9 @@ class LiveService(Service): @property def api(self) -> 'openstack.Client': 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 @@ -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') 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): """ @@ -247,7 +303,7 @@ class LiveService(Service): availabilityZone=self.availabilityZone.value, flavorId=self.flavor.value, networkId=self.network.value, - securityGroupsIdsList=self.securityGroups.value + securityGroupsIdsList=self.securityGroups.value, )['id'] def removeTemplate(self, templateId: str) -> None: @@ -286,7 +342,12 @@ class LiveService(Service): """ server = self.api.getServer(machineId) 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'] def startMachine(self, machineId: str) -> None: @@ -362,8 +423,10 @@ class LiveService(Service): Gets the mac address of first nic of the machine """ 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 = six.next(six.itervalues(net))[0] + 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] return vals['OS-EXT-IPS-MAC:mac_addr'].upper(), vals['addr'] def getBaseName(self) -> str: diff --git a/server/src/uds/services/PhysicalMachines/deployment.py b/server/src/uds/services/PhysicalMachines/deployment.py index 6fc05d6c..a76dbd98 100644 --- a/server/src/uds/services/PhysicalMachines/deployment.py +++ b/server/src/uds/services/PhysicalMachines/deployment.py @@ -81,7 +81,9 @@ class IPMachineDeployed(services.UserDeployment, AutoAttributes): res = dns.resolver.resolve(ip) ip = res[0].address 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 diff --git a/server/src/uds/services/PhysicalMachines/provider.py b/server/src/uds/services/PhysicalMachines/provider.py index ad7fd7fa..a32b2447 100644 --- a/server/src/uds/services/PhysicalMachines/provider.py +++ b/server/src/uds/services/PhysicalMachines/provider.py @@ -148,11 +148,7 @@ class PhysicalMachinesProvider(services.ServiceProvider): config.read_string(self.config.value) for key in config['wol']: if net.ipInNetwork(ip, key): - return ( - config['wol'][key] - .replace('{MAC}', mac) - .replace('{IP}', ip) - ) + return config['wol'][key].replace('{MAC}', mac).replace('{IP}', ip) except Exception as e: logger.error('Error parsing advanced configuration: %s', e) diff --git a/server/src/uds/services/PhysicalMachines/service_base.py b/server/src/uds/services/PhysicalMachines/service_base.py index f446267e..157c3e84 100644 --- a/server/src/uds/services/PhysicalMachines/service_base.py +++ b/server/src/uds/services/PhysicalMachines/service_base.py @@ -16,7 +16,7 @@ # may be used to endorse or promote products derived from this software # 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 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # 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: if mac: - wolurl = ( - self.parent() - .wolURL(ip, mac) - ) + wolurl = self.parent().wolURL(ip, mac) if wolurl: logger.info('Launching WOL: %s', wolurl) try: diff --git a/server/src/uds/services/PhysicalMachines/service_single.py b/server/src/uds/services/PhysicalMachines/service_single.py index 26d40ec5..6a0c6da6 100644 --- a/server/src/uds/services/PhysicalMachines/service_single.py +++ b/server/src/uds/services/PhysicalMachines/service_single.py @@ -51,7 +51,13 @@ logger = logging.getLogger(__name__) class IPSingleMachineService(IPServiceBase): # 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 typeName = _('Static Single IP') @@ -60,7 +66,9 @@ class IPSingleMachineService(IPServiceBase): iconFile = 'machine.png' # 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_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) @@ -75,7 +83,9 @@ class IPSingleMachineService(IPServiceBase): return 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]: ip: typing.Optional[str] = None diff --git a/server/src/uds/services/Proxmox/client/__init__.py b/server/src/uds/services/Proxmox/client/__init__.py index 74f3ba62..fedc8793 100644 --- a/server/src/uds/services/Proxmox/client/__init__.py +++ b/server/src/uds/services/Proxmox/client/__init__.py @@ -260,7 +260,7 @@ class ProxmoxClient: def isVMIdAvailable(self, vmId: int) -> bool: try: self._get(f'cluster/nextid?vmid={vmId}') - except Exception: # Not available + except Exception: # Not available return False return True diff --git a/server/src/uds/services/Proxmox/client/types.py b/server/src/uds/services/Proxmox/client/types.py index 84183c59..5ccd0a9a 100644 --- a/server/src/uds/services/Proxmox/client/types.py +++ b/server/src/uds/services/Proxmox/client/types.py @@ -2,7 +2,7 @@ import datetime import re import typing -networkRe = re.compile(r'([a-zA-Z0-9]+)=([^,]+)') # May have vla id at end +networkRe = re.compile(r'([a-zA-Z0-9]+)=([^,]+)') # May have vla id at end # Conversor from dictionary to NamedTuple conversors: typing.MutableMapping[typing.Type, typing.Callable] = { @@ -13,8 +13,18 @@ conversors: typing.MutableMapping[typing.Type, typing.Callable] = { 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): @@ -28,6 +38,7 @@ class Cluster(typing.NamedTuple): def fromDict(dictionary: typing.MutableMapping[str, typing.Any]) -> 'Cluster': return convertFromDict(Cluster, dictionary) + class Node(typing.NamedTuple): name: str online: bool @@ -41,6 +52,7 @@ class Node(typing.NamedTuple): def fromDict(dictionary: typing.MutableMapping[str, typing.Any]) -> 'Node': return convertFromDict(Node, dictionary) + class NodeStats(typing.NamedTuple): name: str status: str @@ -61,7 +73,20 @@ class NodeStats(typing.NamedTuple): @staticmethod 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): cluster: typing.Optional[Cluster] @@ -80,6 +105,7 @@ class ClusterStatus(typing.NamedTuple): return ClusterStatus(cluster=cluster, nodes=nodes) + class UPID(typing.NamedTuple): node: str pid: int @@ -92,7 +118,7 @@ class UPID(typing.NamedTuple): @staticmethod def fromDict(dictionary: typing.MutableMapping[str, typing.Any]) -> 'UPID': - upid=dictionary['data'] + upid = dictionary['data'] d = upid.split(':') return UPID( node=d[1], @@ -102,9 +128,10 @@ class UPID(typing.NamedTuple): type=d[5], vmid=int(d[6]), user=d[7], - upid=upid + upid=upid, ) + class TaskStatus(typing.NamedTuple): node: str pid: int @@ -133,6 +160,7 @@ class TaskStatus(typing.NamedTuple): def isErrored(self) -> bool: return self.isFinished() and not self.isCompleted() + class NetworkConfiguration(typing.NamedTuple): type: str mac: str @@ -154,7 +182,9 @@ class VMInfo(typing.NamedTuple): template: bool 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] maxdisk: typing.Optional[int] mem: typing.Optional[int] @@ -173,6 +203,7 @@ class VMInfo(typing.NamedTuple): def fromDict(dictionary: typing.MutableMapping[str, typing.Any]) -> 'VMInfo': return convertFromDict(VMInfo, dictionary) + class VMConfiguration(typing.NamedTuple): name: str vga: str @@ -185,7 +216,9 @@ class VMConfiguration(typing.NamedTuple): template: bool @staticmethod - def fromDict(dictionary: typing.MutableMapping[str, typing.Any]) -> 'VMConfiguration': + def fromDict( + dictionary: typing.MutableMapping[str, typing.Any] + ) -> 'VMConfiguration': nets: typing.List[NetworkConfiguration] = [] for k in dictionary.keys(): if k[:3] == 'net': @@ -194,11 +227,13 @@ class VMConfiguration(typing.NamedTuple): dictionary['networks'] = nets return convertFromDict(VMConfiguration, dictionary) + class VmCreationResult(typing.NamedTuple): node: str vmid: int upid: UPID + class StorageInfo(typing.NamedTuple): node: str storage: str @@ -212,11 +247,11 @@ class StorageInfo(typing.NamedTuple): total: int used_fraction: float - @staticmethod def fromDict(dictionary: typing.MutableMapping[str, typing.Any]) -> 'StorageInfo': return convertFromDict(StorageInfo, dictionary) + class PoolInfo(typing.NamedTuple): poolid: str comments: str diff --git a/server/src/uds/services/Proxmox/helpers.py b/server/src/uds/services/Proxmox/helpers.py index bf43112c..a8307f91 100644 --- a/server/src/uds/services/Proxmox/helpers.py +++ b/server/src/uds/services/Proxmox/helpers.py @@ -35,10 +35,12 @@ from django.utils.translation import ugettext as _ logger = logging.getLogger(__name__) + def getStorage(parameters: typing.Any) -> typing.List[typing.Dict[str, typing.Any]]: from .provider import ProxmoxProvider from uds.core.environment import Environment + logger.debug('Parameters received by getResources Helper: %s', parameters) env = Environment(parameters['ev']) provider: ProxmoxProvider = ProxmoxProvider(env) @@ -54,19 +56,27 @@ def getStorage(parameters: typing.Any) -> typing.List[typing.Dict[str, typing.An res = [] # 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'): continue - space, free = storage.avail / 1024 / 1024 / 1024, (storage.avail - storage.used) / 1024 / 1024 / 1024 - extra = _(' shared') if storage.shared else _(' (bound to {})').format(vmInfo.node) - res.append({'id': storage.storage, 'text': "%s (%4.2f GB/%4.2f GB)%s" % (storage.storage, space, free, extra)}) + space, free = ( + storage.avail / 1024 / 1024 / 1024, + (storage.avail - storage.used) / 1024 / 1024 / 1024, + ) + extra = ( + _(' shared') if storage.shared else _(' (bound to {})').format(vmInfo.node) + ) + res.append( + { + 'id': storage.storage, + 'text': "%s (%4.2f GB/%4.2f GB)%s" + % (storage.storage, space, free, extra), + } + ) - data = [ - { - 'name': 'datastore', - 'values': res - } - ] + data = [{'name': 'datastore', 'values': res}] logger.debug('return data: %s', data) return data diff --git a/server/src/uds/services/Proxmox/provider.py b/server/src/uds/services/Proxmox/provider.py index 5f28b27c..9bafe129 100644 --- a/server/src/uds/services/Proxmox/provider.py +++ b/server/src/uds/services/Proxmox/provider.py @@ -52,6 +52,7 @@ logger = logging.getLogger(__name__) CACHE_TIME_FOR_SERVER = 1800 MAX_VM_ID = 999999999 + class ProxmoxProvider( services.ServiceProvider ): # pylint: disable=too-many-public-methods @@ -223,7 +224,14 @@ class ProxmoxProvider( toPool: typing.Optional[str] = None, ) -> client.types.VmCreationResult: 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: diff --git a/server/src/uds/services/Proxmox/publication.py b/server/src/uds/services/Proxmox/publication.py index 1f7e2bb3..a6d78717 100644 --- a/server/src/uds/services/Proxmox/publication.py +++ b/server/src/uds/services/Proxmox/publication.py @@ -45,6 +45,7 @@ if typing.TYPE_CHECKING: logger = logging.getLogger(__name__) + class ProxmoxPublication(services.Publication): suggestedTime = 20 @@ -74,7 +75,18 @@ class ProxmoxPublication(services.Publication): """ 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: """ @@ -83,7 +95,15 @@ class ProxmoxPublication(services.Publication): logger.debug('Data: %s', data) vals = data.decode('utf8').split('\t') 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: """ @@ -91,8 +111,17 @@ class ProxmoxPublication(services.Publication): """ try: # 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()) - comments = _('UDS Publication for {0} created at {1}').format(self.dsName(), str(datetime.now()).split('.')[0]) + self._name = ( + '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) self._vm = str(task.vmid) self._task = ','.join((task.upid.node, task.upid.upid)) @@ -105,7 +134,9 @@ class ProxmoxPublication(services.Publication): self._reason = str(e) 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: return self._state node, upid = self._task.split(',') @@ -122,14 +153,16 @@ class ProxmoxPublication(services.Publication): if task.isErrored(): self._reason = task.exitstatus self._state = State.ERROR - else: # Finished + else: # Finished if self._destroyAfter: return self.destroy() self._state = State.FINISHED if self._operation == 'p': # not Destroying # Disable Protection (removal) 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 self.service().enableHA(int(self._vm)) time.sleep(0.5) @@ -139,7 +172,7 @@ class ProxmoxPublication(services.Publication): # This seems to cause problems on Proxmox # makeTemplate --> setProtection (that calls "config"). Seems that the HD dissapears... # Seems a concurrency problem? - + return self._state def finish(self) -> None: @@ -147,7 +180,9 @@ class ProxmoxPublication(services.Publication): self._destroyAfter = '' 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' return State.RUNNING @@ -166,4 +201,4 @@ class ProxmoxPublication(services.Publication): return self._reason def machine(self) -> int: - return int(self._vm) \ No newline at end of file + return int(self._vm) diff --git a/server/src/uds/services/Proxmox/service.py b/server/src/uds/services/Proxmox/service.py index e49d7a1e..96ca98dd 100644 --- a/server/src/uds/services/Proxmox/service.py +++ b/server/src/uds/services/Proxmox/service.py @@ -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 """ + # : Name to show the administrator. This string will be translated BEFORE # : sending it to administration interface, so don't forget to # : mark it as _ (using ugettext_noop) @@ -103,7 +104,7 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods # : Types of deploys (services in cache and/or assigned to users) deployedType = ProxmoxDeployment - allowedProtocols = protocols.GENERIC # + (protocols.SPICE,) + allowedProtocols = protocols.GENERIC # + (protocols.SPICE,) servicesTypeProvided = (serviceTypes.VDI,) pool = gui.ChoiceField( @@ -112,14 +113,14 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods tooltip=_('Pool that will contain UDS created vms'), # tab=_('Machine'), # required=True, - defvalue='' + defvalue='', ) ha = gui.ChoiceField( label=_('HA'), order=2, tooltip=_('Select if HA is enabled and HA group for machines of this service'), - rdonly=True + rdonly=True, ) guestShutdown = gui.CheckBoxField( @@ -137,11 +138,11 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods fills={ 'callbackName': 'pmFillResourcesFromMachine', 'function': helpers.getStorage, - 'parameters': ['machine', 'ov', 'ev'] + 'parameters': ['machine', 'ov', 'ev'], }, tooltip=_('Service base machine'), tab=_('Machine'), - required=True + required=True, ) datastore = gui.ChoiceField( @@ -150,7 +151,7 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods order=111, tooltip=_('Storage for publications & machines.'), tab=_('Machine'), - required=True + required=True, ) baseName = gui.TextField( @@ -159,7 +160,7 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods order=115, tooltip=_('Base name for clones from this machine'), tab=_('Machine'), - required=True + required=True, ) lenName = gui.NumericField( @@ -169,15 +170,19 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods order=116, tooltip=_('Size of numeric part for the names of these machines'), tab=_('Machine'), - required=True + required=True, ) 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: 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: # 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 # 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.pool.setValues([gui.choiceItem('', _('None'))] + [gui.choiceItem(p.poolid, p.poolid) for p in self.parent().listPools()]) - self.ha.setValues( + self.machine.setValues( [ - gui.choiceItem('', _('Enabled')), gui.choiceItem('__', _('Disabled')) - ] + - [ - gui.choiceItem(group, group) for group in self.parent().listHaGroups() + 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.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': 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: 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) pool = self.pool.value or None if vmId == -1: # vmId == -1 if cloning for template @@ -223,7 +237,7 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods description, linkedClone=False, toStorage=self.datastore.value, - toPool=pool + toPool=pool, ) return self.parent().cloneMachine( @@ -232,7 +246,7 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods description, linkedClone=True, toStorage=self.datastore.value, - toPool=pool + toPool=pool, ) def getMachineInfo(self, vmId: int) -> 'client.types.VMInfo': @@ -245,7 +259,7 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods def getTaskInfo(self, node: str, upid: str) -> 'client.types.TaskStatus': return self.parent().getTaskInfo(node, upid) - def startMachine(self,vmId: int) -> 'client.types.UPID': + def startMachine(self, vmId: int) -> 'client.types.UPID': return self.parent().startMachine(vmId) def stopMachine(self, vmId: int) -> 'client.types.UPID': @@ -276,7 +290,9 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods return 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) def getBaseName(self) -> str: @@ -291,5 +307,7 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods def tryGracelyShutdown(self) -> bool: 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)