diff --git a/server/src/uds/services/Sample/deployment_one.py b/server/src/uds/services/Sample/deployment_one.py index 6d328ce2..2fb12140 100644 --- a/server/src/uds/services/Sample/deployment_one.py +++ b/server/src/uds/services/Sample/deployment_one.py @@ -118,7 +118,9 @@ class SampleUserDeploymentOne(services.UserDeployment): """ name: str = typing.cast(str, self.storage.readData('name')) if name is None: - name = self.nameGenerator().get(self.service().getBaseName() + '-' + self.service().getColour(), 3) + name = self.nameGenerator().get( + self.service().getBaseName() + '-' + self.service().getColour(), 3 + ) # Store value for persistence self.storage.saveData('name', name) @@ -269,7 +271,10 @@ class SampleUserDeploymentOne(services.UserDeployment): destroying, and cancel will simply invoke destroy """ import random - countStr: typing.Optional[str] = typing.cast(str, self.storage.readData('count')) + + countStr: typing.Optional[str] = typing.cast( + str, self.storage.readData('count') + ) count: int = 0 if countStr: count = int(countStr) + 1 diff --git a/server/src/uds/services/Sample/deployment_two.py b/server/src/uds/services/Sample/deployment_two.py index bdc2b890..9d8d79f6 100644 --- a/server/src/uds/services/Sample/deployment_two.py +++ b/server/src/uds/services/Sample/deployment_two.py @@ -119,8 +119,9 @@ class SampleUserDeploymentTwo(services.UserDeployment): beside the values, so we can, at a later stage, treat with old data for current modules. """ - data = '\t'.join(['v1', self._name, self._ip, self._mac, self._error, - str(self._count)]) + data = '\t'.join( + ['v1', self._name, self._ip, self._mac, self._error, str(self._count)] + ) return codecs.encode(data.encode(), encoding='zip') # type: ignore def unmarshal(self, data: bytes) -> None: diff --git a/server/src/uds/services/Sample/provider.py b/server/src/uds/services/Sample/provider.py index 3074fbe3..769719c0 100644 --- a/server/src/uds/services/Sample/provider.py +++ b/server/src/uds/services/Sample/provider.py @@ -66,6 +66,7 @@ class Provider(services.ServiceProvider): we MUST register it at package __init__. """ + # : What kind of services we offer, this are classes inherited from Service offers = [ServiceOne, ServiceTwo] # : Name to show the administrator. This string will be translated BEFORE @@ -107,7 +108,7 @@ class Provider(services.ServiceProvider): label=_('Your pet\'s name'), tooltip=_('If you like, write the name of your pet'), requred=False, - defvalue='Tux' # : This will not get translated + defvalue='Tux', # : This will not get translated ) # : Age of Methuselah (matusalén in spanish) # : in Spain there is a well-known to say that something is very old, @@ -118,7 +119,7 @@ class Provider(services.ServiceProvider): label=_('Age of Methuselah'), tooltip=_('If you know it, please, tell me!!!'), required=True, # : Numeric fields have always a value, so this not really needed - defvalue='4500' + defvalue='4500', ) # : Is Methuselah istill alive? @@ -126,7 +127,7 @@ class Provider(services.ServiceProvider): order=4, label=_('Is Methuselah still alive?'), tooltip=_('If you fail, this will not get saved :-)'), - defvalue=gui.TRUE # : By default, at new item, check this + defvalue=gui.TRUE, # : By default, at new item, check this ) methText = gui.TextField( @@ -136,7 +137,7 @@ class Provider(services.ServiceProvider): label=_('Text area'), tooltip=_('This is a text area'), requred=False, - defvalue='Write\nsomething' # : This will not get translated + defvalue='Write\nsomething', # : This will not get translated ) # There is more fields type, but not here the best place to cover it @@ -154,7 +155,9 @@ class Provider(services.ServiceProvider): # values are only passed from administration client. Internals # instantiations are always empty. if values and self.methAlive.isTrue(): - raise services.ServiceProvider.ValidationException(_('Methuselah is not alive!!! :-)')) + raise services.ServiceProvider.ValidationException( + _('Methuselah is not alive!!! :-)') + ) # Marshal and unmarshal are defaults ones, also enought @@ -162,7 +165,9 @@ class Provider(services.ServiceProvider): # base class so we don't have to mess with all those things... @staticmethod - def test(env: 'Environment', data: typing.Dict[str, str]) -> typing.List[typing.Any]: + def test( + env: 'Environment', data: typing.Dict[str, str] + ) -> typing.List[typing.Any]: """ Create your test method here so the admin can push the "check" button and this gets executed. @@ -186,7 +191,11 @@ class Provider(services.ServiceProvider): try: # We instantiate the provider, but this may fail... instance = Provider(env, data) - logger.debug('Methuselah has %s years and is %s :-)', instance.methAge.value, instance.methAlive.value) + logger.debug( + 'Methuselah has %s years and is %s :-)', + instance.methAge.value, + instance.methAlive.value, + ) except services.ServiceProvider.ValidationException as e: # If we say that meth is alive, instantiation will return [False, str(e)] diff --git a/server/src/uds/services/Sample/publication.py b/server/src/uds/services/Sample/publication.py index 36542757..43847776 100644 --- a/server/src/uds/services/Sample/publication.py +++ b/server/src/uds/services/Sample/publication.py @@ -84,7 +84,9 @@ class SamplePublication(services.Publication): it's expressed in seconds, (i.e. "suggestedTime = 10") """ - suggestedTime = 5 # : Suggested recheck time if publication is unfinished in seconds + suggestedTime = ( + 5 # : Suggested recheck time if publication is unfinished in seconds + ) _name: str = '' _reason: str = '' _number: int = -1 @@ -217,7 +219,9 @@ class SamplePublication(services.Publication): Returned value, if any, is ignored """ # Make simply a random string - self._name = ''.join(random.SystemRandom().choices(string.ascii_uppercase + string.digits, k=10)) + self._name = ''.join( + random.SystemRandom().choices(string.ascii_uppercase + string.digits, k=10) + ) def reasonOfError(self) -> str: """ diff --git a/server/src/uds/services/Sample/service.py b/server/src/uds/services/Sample/service.py index b62e38d9..56a9b1ee 100644 --- a/server/src/uds/services/Sample/service.py +++ b/server/src/uds/services/Sample/service.py @@ -49,6 +49,7 @@ if typing.TYPE_CHECKING: logger = logging.getLogger(__name__) + class ServiceOne(services.Service): """ Basic service, the first part (variables) include the description of the service. @@ -71,6 +72,7 @@ class ServiceOne(services.Service): information. """ + # : 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) @@ -130,9 +132,9 @@ class ServiceOne(services.Service): gui.choiceItem('red', 'Red'), gui.choiceItem('green', 'Green'), gui.choiceItem('blue', 'Blue'), - gui.choiceItem('nonsense', 'Blagenta') + gui.choiceItem('nonsense', 'Blagenta'), ], - defvalue='1' # Default value is the ID of the choicefield + defvalue='1', # Default value is the ID of the choicefield ) passw = gui.PasswordField( @@ -140,7 +142,7 @@ class ServiceOne(services.Service): label=_('Password'), tooltip=_('Password for testing purposes'), required=True, - defvalue='1234' # : Default password are nonsense?? :-) + defvalue='1234', # : Default password are nonsense?? :-) ) baseName = gui.TextField( @@ -149,7 +151,7 @@ class ServiceOne(services.Service): tooltip=_('Base name for this user services'), # In this case, the choice can have none value selected by default required=True, - defvalue='' # Default value is the ID of the choicefield + defvalue='', # Default value is the ID of the choicefield ) def initialize(self, values: 'Module.ValuesType') -> None: @@ -165,13 +167,13 @@ class ServiceOne(services.Service): # so we only need to validate params if values is not None if values: if self.colour.value == 'nonsense': - raise services.Service.ValidationException('The selected colour is invalid!!!') - + raise services.Service.ValidationException( + 'The selected colour is invalid!!!' + ) # Services itself are non testeable right now, so we don't even have # to provide one!!! - # Congratulations!!!, the needed part of your first simple service is done! # Now you can go to administration panel, and check it # @@ -200,6 +202,7 @@ class ServiceTwo(services.Service): """ Just a second service, no comments here (almost same that ServiceOne """ + typeName = _('Sample Service Two') typeType = 'SampleService2' typeDescription = _('Sample (and dummy) service ONE+ONE') @@ -222,7 +225,6 @@ class ServiceTwo(services.Service): # : Types of deploys (services in cache and/or assigned to users) deployedType = SampleUserDeploymentTwo - # Gui, we will use here the EditableList field names = gui.EditableList(label=_('List of names')) diff --git a/server/src/uds/services/Xen/deployment.py b/server/src/uds/services/Xen/deployment.py index f069805b..d6f232a2 100644 --- a/server/src/uds/services/Xen/deployment.py +++ b/server/src/uds/services/Xen/deployment.py @@ -48,7 +48,20 @@ if typing.TYPE_CHECKING: logger = logging.getLogger(__name__) -opCreate, opStart, opStop, opSuspend, opRemove, opWait, opError, opFinish, opRetry, opConfigure, opProvision, opWaitSuspend = range(12) +( + opCreate, + opStart, + opStop, + opSuspend, + opRemove, + opWait, + opError, + opFinish, + opRetry, + opConfigure, + opProvision, + opWaitSuspend, +) = range(12) NO_MORE_NAMES = 'NO-NAME-ERROR' @@ -80,16 +93,18 @@ class XenLinkedDeployment(UserDeployment): # 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), - self._task.encode('utf8') - ]) + 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), + self._task.encode('utf8'), + ] + ) def unmarshal(self, data: bytes) -> None: vals = data.split(b'\1') @@ -106,7 +121,9 @@ class XenLinkedDeployment(UserDeployment): def getName(self) -> str: if not 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 @@ -168,7 +185,16 @@ class XenLinkedDeployment(UserDeployment): if forLevel2 is False: self._queue = [opCreate, opConfigure, opProvision, opStart, opFinish] else: - self._queue = [opCreate, opConfigure, opProvision, opStart, opWait, opWaitSuspend, opSuspend, opFinish] + self._queue = [ + opCreate, + opConfigure, + opProvision, + opStart, + opWait, + opWaitSuspend, + opSuspend, + opFinish, + ] def __getCurrentOp(self) -> int: if len(self._queue) == 0: @@ -196,7 +222,11 @@ class XenLinkedDeployment(UserDeployment): if self._vmid != '': # Powers off and delete VM try: state = self.service().getVMPowerState(self._vmid) - if state in (XenPowerState.running, XenPowerState.paused, XenPowerState.suspended): + if state in ( + XenPowerState.running, + XenPowerState.paused, + XenPowerState.suspended, + ): self.service().stopVM(self._vmid, False) # In sync mode self.service().removeVM(self._vmid) except Exception: @@ -226,14 +256,16 @@ class XenLinkedDeployment(UserDeployment): opWait: self.__wait, opRemove: self.__remove, opConfigure: self.__configure, - opProvision: self.__provision + opProvision: self.__provision, } 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() @@ -265,9 +297,13 @@ class XenLinkedDeployment(UserDeployment): 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 = 'UDS service ' + self.service().sanitizeVmName(name) # oVirt don't let us to create machines with more than 15 chars!!! + name = 'UDS service ' + self.service().sanitizeVmName( + name + ) # oVirt don't let us to create machines with more than 15 chars!!! comments = 'UDS Linked clone' self._task = self.service().startDeployFromTemplate(name, comments, templateId) @@ -282,7 +318,7 @@ class XenLinkedDeployment(UserDeployment): """ state = self.service().getVMPowerState(self._vmid) - if state not in(XenPowerState.halted, XenPowerState.suspended): + if state not in (XenPowerState.halted, XenPowerState.suspended): self.__pushFrontOp(opStop) self.__executeQueue() else: @@ -348,7 +384,9 @@ class XenLinkedDeployment(UserDeployment): """ Makes machine usable on Xen """ - self.service().provisionVM(self._vmid, False) # Let's try this in "sync" mode, this must be fast enough + self.service().provisionVM( + self._vmid, False + ) # Let's try this in "sync" mode, this must be fast enough return State.RUNNING @@ -434,14 +472,16 @@ class XenLinkedDeployment(UserDeployment): opSuspend: self.__checkSuspend, opRemove: self.__checkRemoved, opConfigure: self.__checkConfigure, - opProvision: self.__checkProvision + opProvision: self.__checkProvision, } try: chkFnc: typing.Optional[typing.Callable[[], str]] = fncs.get(op, None) if chkFnc is None: - return self.__error('Unknown operation found at check queue ({})'.format(op)) + return self.__error( + 'Unknown operation found at check queue ({})'.format(op) + ) state = chkFnc() if state == State.FINISHED: @@ -503,8 +543,16 @@ class XenLinkedDeployment(UserDeployment): opFinish: 'finish', opRetry: 'retry', opConfigure: 'configuring', - opProvision: 'provisioning' + opProvision: 'provisioning', }.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, [XenLinkedDeployment.__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, + [XenLinkedDeployment.__op2str(op) for op in self._queue], + ) diff --git a/server/src/uds/services/Xen/publication.py b/server/src/uds/services/Xen/publication.py index 26510a64..f091934e 100644 --- a/server/src/uds/services/Xen/publication.py +++ b/server/src/uds/services/Xen/publication.py @@ -45,7 +45,9 @@ logger = logging.getLogger(__name__) class XenPublication(Publication): - 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 = '' @@ -61,23 +63,44 @@ class XenPublication(Publication): """ returns data from an instance of Sample Publication serialized """ - return '\t'.join(['v1', self._name, self._reason, self._destroyAfter, self._templateId, self._state, self._task]).encode('utf8') + return '\t'.join( + [ + 'v1', + self._name, + self._reason, + self._destroyAfter, + self._templateId, + self._state, + self._task, + ] + ).encode('utf8') def unmarshal(self, data: bytes) -> None: """ deserializes the data and loads it inside instance. """ - #logger.debug('Data: {0}'.format(data)) + # logger.debug('Data: {0}'.format(data)) vals = data.decode('utf8').split('\t') if vals[0] == 'v1': - self._name, self._reason, self._destroyAfter, self._templateId, self._state, self._task = vals[1:] + ( + self._name, + self._reason, + self._destroyAfter, + self._templateId, + self._state, + self._task, + ) = vals[1:] def publish(self) -> str: """ Realizes the publication of the service """ - self._name = self.service().sanitizeVmName('UDS Pub ' + 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( + 'UDS Pub ' + 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 = 'ok' diff --git a/server/src/uds/services/Xen/service.py b/server/src/uds/services/Xen/service.py index 48f450be..545df345 100644 --- a/server/src/uds/services/Xen/service.py +++ b/server/src/uds/services/Xen/service.py @@ -54,6 +54,7 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods """ + # : 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) @@ -78,15 +79,13 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods usesCache = True # : Tooltip shown to user when this item is pointed at admin interface, none # : because we don't use it - cacheTooltip = _( - 'Number of desired machines to keep running waiting for a user') + cacheTooltip = _('Number of desired machines to keep running waiting for a user') # : If we need to generate a "Level 2" cache for this service (i.e., L1 # : could be running machines and L2 suspended machines) usesCache_L2 = True # : Tooltip shown to user when this item is pointed at admin interface, None # : also because we don't use it - cacheTooltip_L2 = _( - 'Number of desired machines to keep suspended waiting for use') + cacheTooltip_L2 = _('Number of desired machines to keep suspended waiting for use') # : If the service needs a s.o. manager (managers are related to agents # : provided by services itselfs, i.e. virtual machines with actors) @@ -109,8 +108,10 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods label=_("Storage SR"), rdonly=False, order=100, - tooltip=_('Storage where to publish and put incrementals (only shared storages are supported)'), - required=True + tooltip=_( + 'Storage where to publish and put incrementals (only shared storages are supported)' + ), + required=True, ) minSpaceGB = gui.NumericField( @@ -119,7 +120,7 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods defvalue='32', order=101, tooltip=_('Minimal free space in GB'), - required=True + required=True, ) machine = gui.ChoiceField( @@ -127,7 +128,7 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods order=110, tooltip=_('Service base machine'), tab=_('Machine'), - required=True + required=True, ) network = gui.ChoiceField( @@ -136,17 +137,18 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods order=111, tooltip=_('Network used for virtual machines'), tab=_('Machine'), - required=True + required=True, ) memory = gui.NumericField( label=_("Memory (Mb)"), - length=4, defvalue=512, + length=4, + defvalue=512, rdonly=False, order=112, tooltip=_('Memory assigned to machines'), tab=_('Machine'), - required=True + required=True, ) shadow = gui.NumericField( @@ -157,7 +159,7 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods order=113, tooltip=_('Shadow memory multiplier (use with care)'), tab=_('Machine'), - required=True + required=True, ) baseName = gui.TextField( @@ -166,7 +168,7 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods order=114, tooltip=_('Base name for clones from this machine'), tab=_('Machine'), - required=True + required=True, ) lenName = gui.NumericField( @@ -176,7 +178,7 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods order=115, tooltip=_('Size of numeric part for the names of these machines'), tab=_('Machine'), - required=True + required=True, ) def initialize(self, values): @@ -191,7 +193,8 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods if int(self.memory.value) < 256: raise Service.ValidationException( - _('The minimum allowed memory is 256 Mb')) + _('The minimum allowed memory is 256 Mb') + ) def parent(self) -> 'XenProvider': return typing.cast('XenProvider', super().parent()) @@ -201,14 +204,27 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods # This is that value is always '', so if we want to change something, we have to do it # at defValue - machines_list = [gui.choiceItem(m['id'], m['name']) for m in self.parent().getMachines()] + machines_list = [ + gui.choiceItem(m['id'], m['name']) for m in self.parent().getMachines() + ] storages_list = [] for storage in self.parent().getStorages(): - space, free = storage['size'] / 1024, (storage['size'] - storage['used']) / 1024 - storages_list.append(gui.choiceItem(storage['id'], "%s (%4.2f Gb/%4.2f Gb)" % (storage['name'], space, free))) + space, free = ( + storage['size'] / 1024, + (storage['size'] - storage['used']) / 1024, + ) + storages_list.append( + gui.choiceItem( + storage['id'], + "%s (%4.2f Gb/%4.2f Gb)" % (storage['name'], space, free), + ) + ) - network_list = [gui.choiceItem(net['id'], net['name']) for net in self.parent().getNetworks()] + network_list = [ + gui.choiceItem(net['id'], net['name']) + for net in self.parent().getNetworks() + ] self.machine.setValues(machines_list) self.datastore.setValues(storages_list) @@ -223,7 +239,11 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods logger.debug('Checking datastore space for %s: %s', self.datastore.value, info) availableGB = (info['size'] - info['used']) / 1024 if availableGB < self.minSpaceGB.num(): - raise Exception('Not enough free space available: (Needs at least {} GB and there is only {} GB '.format(self.minSpaceGB.num(), availableGB)) + raise Exception( + 'Not enough free space available: (Needs at least {} GB and there is only {} GB '.format( + self.minSpaceGB.num(), availableGB + ) + ) def sanitizeVmName(self, name: str) -> str: """ @@ -245,12 +265,18 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods Raises an exception if operation fails. """ - logger.debug('Starting deploy of template from machine %s on datastore %s', self.machine.value, self.datastore.value) + logger.debug( + 'Starting deploy of template from machine %s on datastore %s', + self.machine.value, + self.datastore.value, + ) # Checks datastore available space, raises exeception in no min available self.datastoreHasSpace() - return self.parent().cloneForTemplate(name, comments, self.machine.value, self.datastore.value) + return self.parent().cloneForTemplate( + name, comments, self.machine.value, self.datastore.value + ) def convertToTemplate(self, machineId: str) -> None: """ diff --git a/server/src/uds/services/Xen/xen_client/XenAPI.py b/server/src/uds/services/Xen/xen_client/XenAPI.py index 23848889..65351bb5 100644 --- a/server/src/uds/services/Xen/xen_client/XenAPI.py +++ b/server/src/uds/services/Xen/xen_client/XenAPI.py @@ -69,8 +69,10 @@ translation = gettext.translation('xen-xm', fallback=True) API_VERSION_1_1 = '1.1' API_VERSION_1_2 = '1.2' + class Failure(Exception): details: typing.List[typing.Any] + def __init__(self, details: typing.List[typing.Any]): super().__init__() self.details = details @@ -87,19 +89,24 @@ class Failure(Exception): # dict([(str(i), self.details[i]) for i in range(len(self.details))]) return {str(i): d for i, d in enumerate(self.details)} + # Just a "constant" that we use to decide whether to retry the RPC _RECONNECT_AND_RETRY = object() + class UDSHTTPConnection(httplib.HTTPConnection): - """HTTPConnection subclass to allow HTTP over Unix domain sockets. """ + """HTTPConnection subclass to allow HTTP over Unix domain sockets.""" + def connect(self): path = self.host.replace("_", "/") self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.sock.connect(path) + class UDSHTTP(httplib.HTTPConnection): _connection_class = UDSHTTPConnection + class UDSTransport(xmlrpclib.Transport): _use_datetime: bool _extra_headers: typing.List[typing.Tuple[str, str]] @@ -117,11 +124,14 @@ class UDSTransport(xmlrpclib.Transport): def make_connection(self, host: str) -> httplib.HTTPConnection: # type: ignore # In our case, host is always an string return UDSHTTPConnection(host) - def send_request(self, connection, handler, request_body, debug): # pylint: disable=arguments-differ + def send_request( + self, connection, handler, request_body, debug + ): # pylint: disable=arguments-differ connection.putrequest("POST", handler) for key, value in self._extra_headers: connection.putheader(key, value) + class Session(xmlrpclib.ServerProxy): """A server proxy and session manager for communicating with xapi using the Xen-API. @@ -134,25 +144,40 @@ class Session(xmlrpclib.ServerProxy): session.xenapi.session.logout() """ - def __init__(self, uri, transport=None, encoding=None, verbose=0, - allow_none=1, ignore_ssl=False): + def __init__( + self, + uri, + transport=None, + encoding=None, + verbose=0, + allow_none=1, + ignore_ssl=False, + ): # Fix for CA-172901 (+ Python 2.4 compatibility) # Fix for context=ctx ( < Python 2.7.9 compatibility) - if not (sys.version_info[0] <= 2 and sys.version_info[1] <= 7 and sys.version_info[2] <= 9) and ignore_ssl: + if ( + not ( + sys.version_info[0] <= 2 + and sys.version_info[1] <= 7 + and sys.version_info[2] <= 9 + ) + and ignore_ssl + ): ctx = ssl._create_unverified_context() - xmlrpclib.ServerProxy.__init__(self, uri, transport, encoding, - verbose, allow_none, context=ctx) + xmlrpclib.ServerProxy.__init__( + self, uri, transport, encoding, verbose, allow_none, context=ctx + ) else: - xmlrpclib.ServerProxy.__init__(self, uri, transport, encoding, - verbose, allow_none) + xmlrpclib.ServerProxy.__init__( + self, uri, transport, encoding, verbose, allow_none + ) self.transport = transport self._session = None self.last_login_method = None self.last_login_params = None self.API_version = API_VERSION_1_1 - def xenapi_request(self, methodname, params): if methodname.startswith('login'): self._login(methodname, params) @@ -168,22 +193,20 @@ class Session(xmlrpclib.ServerProxy): if result is _RECONNECT_AND_RETRY: retry_count += 1 if self.last_login_method: - self._login(self.last_login_method, - self.last_login_params) + self._login(self.last_login_method, self.last_login_params) else: raise xmlrpclib.Fault(401, 'You must log in') else: return result raise xmlrpclib.Fault( - 500, 'Tried 3 times to get a valid session, but failed') + 500, 'Tried 3 times to get a valid session, but failed' + ) def _login(self, method, params): try: - result = _parse_result( - getattr(self, 'session.%s' % method)(*params)) + result = _parse_result(getattr(self, 'session.%s' % method)(*params)) if result is _RECONNECT_AND_RETRY: - raise xmlrpclib.Fault( - 500, 'Received SESSION_INVALID when logging in') + raise xmlrpclib.Fault(500, 'Received SESSION_INVALID when logging in') self._session = result self.last_login_method = method self.last_login_params = params @@ -195,7 +218,7 @@ class Session(xmlrpclib.ServerProxy): def _logout(self): try: - if self.last_login_method.startswith("slave_local"): + if self.last_login_method.startswith("slave_local"): # type: ignore return _parse_result(self.session.local_logout(self._session)) # type: ignore else: return _parse_result(self.session.logout(self._session)) # type: ignore @@ -210,7 +233,7 @@ class Session(xmlrpclib.ServerProxy): host = self.xenapi.pool.get_master(pool) major = self.xenapi.host.get_API_version_major(host) minor = self.xenapi.host.get_API_version_minor(host) - return "%s.%s"%(major, minor) + return "%s.%s" % (major, minor) def __getattr__(self, name): if name == 'handle': @@ -224,12 +247,16 @@ class Session(xmlrpclib.ServerProxy): else: return xmlrpclib.ServerProxy.__getattr__(self, name) + def xapi_local(): return Session("http://_var_lib_xcp_xapi/", transport=UDSTransport()) + def _parse_result(result): if not isinstance(result, dict) or 'Status' not in result: - raise xmlrpclib.Fault(500, 'Missing Status in response from server: {}'.format(result)) + raise xmlrpclib.Fault( + 500, 'Missing Status in response from server: {}'.format(result) + ) if result['Status'] == 'Success': if 'Value' in result: return result['Value'] @@ -258,7 +285,9 @@ class _Dispatcher: if self.__name is None: return _Dispatcher(self.__API_version, self.__send, name) else: - return _Dispatcher(self.__API_version, self.__send, "%s.%s" % (self.__name, name)) + return _Dispatcher( + self.__API_version, self.__send, "%s.%s" % (self.__name, name) + ) def __call__(self, *args): return self.__send(self.__name, args) diff --git a/server/src/uds/services/Xen/xen_client/__init__.py b/server/src/uds/services/Xen/xen_client/__init__.py index a5a04987..6c87843d 100644 --- a/server/src/uds/services/Xen/xen_client/__init__.py +++ b/server/src/uds/services/Xen/xen_client/__init__.py @@ -38,6 +38,7 @@ logger = logging.getLogger(__name__) TAG_TEMPLATE = "uds-template" TAG_MACHINE = "uds-machine" + class XenFault(Exception): pass @@ -113,7 +114,16 @@ class XenServer: # pylint: disable=too-many-public-methods _poolName: str _apiVersion: str - def __init__(self, host: str, host_backup: str, port: int, username: str, password: str, useSSL: bool = False, verifySSL: bool = False): + def __init__( + self, + host: str, + host_backup: str, + port: int, + username: str, + password: str, + useSSL: bool = False, + verifySSL: bool = False, + ): self._originalHost = self._host = host self._host_backup = host_backup or '' self._port = str(port) @@ -186,7 +196,11 @@ class XenServer: # pylint: disable=too-many-public-methods self._poolName = str(self.getPoolName()) except XenAPI.Failure as e: # XenAPI.Failure: ['HOST_IS_SLAVE', '172.27.0.29'] indicates that this host is an slave of 172.27.0.29, connect to it... if switchToMaster and e.details[0] == 'HOST_IS_SLAVE': - logger.info('%s is an Slave, connecting to master at %s', self._host, e.details[1]) + logger.info( + '%s is an Slave, connecting to master at %s', + self._host, + e.details[1], + ) self._host = e.details[1] self.login() else: @@ -246,7 +260,11 @@ class XenServer: # pylint: disable=too-many-public-methods status = 'failure' # Removes if present - if result and not isinstance(result, XenFailure) and result.startswith(''): + if ( + result + and not isinstance(result, XenFailure) + and result.startswith('') + ): result = result[7:-8] if destroyTask: @@ -255,14 +273,18 @@ class XenServer: # pylint: disable=too-many-public-methods except Exception as e: logger.warning('Destroy task %s returned error %s', task, str(e)) - return {'result': result, 'progress': progress, 'status':str(status)} + return {'result': result, 'progress': progress, 'status': str(status)} def getSRs(self) -> typing.Iterable[typing.MutableMapping[str, typing.Any]]: for srId in self.SR.get_all(): # Only valid SR shared, non iso name_label = self.SR.get_name_label(srId) # Skip non valid... - if self.SR.get_content_type(srId) == 'iso' or self.SR.get_shared(srId) is False or name_label == '': + if ( + self.SR.get_content_type(srId) == 'iso' + or self.SR.get_shared(srId) is False + or name_label == '' + ): continue valid = True @@ -276,7 +298,7 @@ class XenServer: # pylint: disable=too-many-public-methods 'id': srId, 'name': name_label, 'size': XenServer.toMb(self.SR.get_physical_size(srId)), - 'used': XenServer.toMb(self.SR.get_physical_utilisation(srId)) + 'used': XenServer.toMb(self.SR.get_physical_utilisation(srId)), } def getSRInfo(self, srId: str) -> typing.MutableMapping[str, typing.Any]: @@ -284,22 +306,24 @@ class XenServer: # pylint: disable=too-many-public-methods 'id': srId, 'name': self.SR.get_name_label(srId), 'size': XenServer.toMb(self.SR.get_physical_size(srId)), - 'used': XenServer.toMb(self.SR.get_physical_utilisation(srId)) + 'used': XenServer.toMb(self.SR.get_physical_utilisation(srId)), } def getNetworks(self) -> typing.Iterable[typing.MutableMapping[str, typing.Any]]: for netId in self.network.get_all(): - if self.network.get_other_config(netId).get('is_host_internal_management_network', False) is False: + if ( + self.network.get_other_config(netId).get( + 'is_host_internal_management_network', False + ) + is False + ): yield { 'id': netId, 'name': self.network.get_name_label(netId), } def getNetworkInfo(self, netId: str) -> typing.MutableMapping[str, typing.Any]: - return { - 'id': netId, - 'name': self.network.get_name_label(netId) - } + return {'id': netId, 'name': self.network.get_name_label(netId)} def getVMs(self) -> typing.Iterable[typing.MutableMapping[str, typing.Any]]: try: @@ -308,8 +332,7 @@ class XenServer: # pylint: disable=too-many-public-methods # if self.VM.get_is_a_template(vm): # Sample set_tags, easy.. # self.VM.set_tags(vm, ['template']) # continue - if self.VM.get_is_control_domain(vm) or \ - self.VM.get_is_a_template(vm): + if self.VM.get_is_control_domain(vm) or self.VM.get_is_a_template(vm): continue yield {'id': vm, 'name': self.VM.get_name_label(vm)} @@ -382,7 +405,9 @@ class XenServer: # pylint: disable=too-many-public-methods return self.Async.VM.resume(vmId, False, False) return self.VM.resume(vmId, False, False) - def cloneVM(self, vmId: str, targetName: str, targetSR: typing.Optional[str] = None) -> str: + def cloneVM( + self, vmId: str, targetName: str, targetSR: typing.Optional[str] = None + ) -> str: """ If targetSR is NONE: Clones the specified VM, making a new VM. @@ -402,11 +427,15 @@ class XenServer: # pylint: disable=too-many-public-methods try: if targetSR: if 'copy' not in operations: - raise XenException('Copy is not supported for this machine (maybe it\'s powered on?)') + raise XenException( + 'Copy is not supported for this machine (maybe it\'s powered on?)' + ) task = self.Async.VM.copy(vmId, targetName, targetSR) else: if 'clone' not in operations: - raise XenException('Clone is not supported for this machine (maybe it\'s powered on?)') + raise XenException( + 'Clone is not supported for this machine (maybe it\'s powered on?)' + ) task = self.Async.VM.clone(vmId, targetName) return task except XenAPI.Failure as e: @@ -490,7 +519,9 @@ class XenServer: # pylint: disable=too-many-public-methods operations = self.VM.get_allowed_operations(vmId) logger.debug('Allowed operations: %s', operations) if 'make_into_template' not in operations: - raise XenException('Convert in template is not supported for this machine') + raise XenException( + 'Convert in template is not supported for this machine' + ) self.VM.set_is_a_template(vmId, True) # Apply that is an "UDS Template" taggint it diff --git a/server/src/uds/services/__init__.py b/server/src/uds/services/__init__.py index 574786e2..f0cd4750 100644 --- a/server/src/uds/services/__init__.py +++ b/server/src/uds/services/__init__.py @@ -63,7 +63,7 @@ def __init__(): for _, name, _ in pkgutil.iter_modules([pkgpath]): # __import__('uds.services.' + name, globals(), locals(), []) importlib.import_module('.' + name, __name__) # import module - + importlib.invalidate_caches() for p in [services.ServiceProvider]: