More formating

This commit is contained in:
Adolfo Gómez García 2021-08-23 14:59:07 +02:00
parent 1b7076e645
commit c72bcf4200
11 changed files with 291 additions and 113 deletions

View File

@ -118,7 +118,9 @@ class SampleUserDeploymentOne(services.UserDeployment):
""" """
name: str = typing.cast(str, self.storage.readData('name')) name: str = typing.cast(str, self.storage.readData('name'))
if name is None: 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 # Store value for persistence
self.storage.saveData('name', name) self.storage.saveData('name', name)
@ -269,7 +271,10 @@ class SampleUserDeploymentOne(services.UserDeployment):
destroying, and cancel will simply invoke destroy destroying, and cancel will simply invoke destroy
""" """
import random 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 count: int = 0
if countStr: if countStr:
count = int(countStr) + 1 count = int(countStr) + 1

View File

@ -119,8 +119,9 @@ class SampleUserDeploymentTwo(services.UserDeployment):
beside the values, so we can, at a later stage, treat with old beside the values, so we can, at a later stage, treat with old
data for current modules. data for current modules.
""" """
data = '\t'.join(['v1', self._name, self._ip, self._mac, self._error, data = '\t'.join(
str(self._count)]) ['v1', self._name, self._ip, self._mac, self._error, str(self._count)]
)
return codecs.encode(data.encode(), encoding='zip') # type: ignore return codecs.encode(data.encode(), encoding='zip') # type: ignore
def unmarshal(self, data: bytes) -> None: def unmarshal(self, data: bytes) -> None:

View File

@ -66,6 +66,7 @@ class Provider(services.ServiceProvider):
we MUST register it at package __init__. we MUST register it at package __init__.
""" """
# : What kind of services we offer, this are classes inherited from Service # : What kind of services we offer, this are classes inherited from Service
offers = [ServiceOne, ServiceTwo] offers = [ServiceOne, ServiceTwo]
# : Name to show the administrator. This string will be translated BEFORE # : Name to show the administrator. This string will be translated BEFORE
@ -107,7 +108,7 @@ class Provider(services.ServiceProvider):
label=_('Your pet\'s name'), label=_('Your pet\'s name'),
tooltip=_('If you like, write the name of your pet'), tooltip=_('If you like, write the name of your pet'),
requred=False, requred=False,
defvalue='Tux' # : This will not get translated defvalue='Tux', # : This will not get translated
) )
# : Age of Methuselah (matusalén in spanish) # : Age of Methuselah (matusalén in spanish)
# : in Spain there is a well-known to say that something is very old, # : 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'), label=_('Age of Methuselah'),
tooltip=_('If you know it, please, tell me!!!'), tooltip=_('If you know it, please, tell me!!!'),
required=True, # : Numeric fields have always a value, so this not really needed required=True, # : Numeric fields have always a value, so this not really needed
defvalue='4500' defvalue='4500',
) )
# : Is Methuselah istill alive? # : Is Methuselah istill alive?
@ -126,7 +127,7 @@ class Provider(services.ServiceProvider):
order=4, order=4,
label=_('Is Methuselah still alive?'), label=_('Is Methuselah still alive?'),
tooltip=_('If you fail, this will not get saved :-)'), 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( methText = gui.TextField(
@ -136,7 +137,7 @@ class Provider(services.ServiceProvider):
label=_('Text area'), label=_('Text area'),
tooltip=_('This is a text area'), tooltip=_('This is a text area'),
requred=False, 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 # 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 # values are only passed from administration client. Internals
# instantiations are always empty. # instantiations are always empty.
if values and self.methAlive.isTrue(): 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 # 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... # base class so we don't have to mess with all those things...
@staticmethod @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 Create your test method here so the admin can push the "check" button
and this gets executed. and this gets executed.
@ -186,7 +191,11 @@ class Provider(services.ServiceProvider):
try: try:
# We instantiate the provider, but this may fail... # We instantiate the provider, but this may fail...
instance = Provider(env, data) 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: except services.ServiceProvider.ValidationException as e:
# If we say that meth is alive, instantiation will # If we say that meth is alive, instantiation will
return [False, str(e)] return [False, str(e)]

View File

@ -84,7 +84,9 @@ class SamplePublication(services.Publication):
it's expressed in seconds, (i.e. "suggestedTime = 10") 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 = '' _name: str = ''
_reason: str = '' _reason: str = ''
_number: int = -1 _number: int = -1
@ -217,7 +219,9 @@ class SamplePublication(services.Publication):
Returned value, if any, is ignored Returned value, if any, is ignored
""" """
# Make simply a random string # 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: def reasonOfError(self) -> str:
""" """

View File

@ -49,6 +49,7 @@ if typing.TYPE_CHECKING:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class ServiceOne(services.Service): class ServiceOne(services.Service):
""" """
Basic service, the first part (variables) include the description of the service. Basic service, the first part (variables) include the description of the service.
@ -71,6 +72,7 @@ class ServiceOne(services.Service):
information. information.
""" """
# : Name to show the administrator. This string will be translated BEFORE # : Name to show the administrator. This string will be translated BEFORE
# : sending it to administration interface, so don't forget to # : sending it to administration interface, so don't forget to
# : mark it as _ (using ugettext_noop) # : mark it as _ (using ugettext_noop)
@ -130,9 +132,9 @@ class ServiceOne(services.Service):
gui.choiceItem('red', 'Red'), gui.choiceItem('red', 'Red'),
gui.choiceItem('green', 'Green'), gui.choiceItem('green', 'Green'),
gui.choiceItem('blue', 'Blue'), 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( passw = gui.PasswordField(
@ -140,7 +142,7 @@ class ServiceOne(services.Service):
label=_('Password'), label=_('Password'),
tooltip=_('Password for testing purposes'), tooltip=_('Password for testing purposes'),
required=True, required=True,
defvalue='1234' # : Default password are nonsense?? :-) defvalue='1234', # : Default password are nonsense?? :-)
) )
baseName = gui.TextField( baseName = gui.TextField(
@ -149,7 +151,7 @@ class ServiceOne(services.Service):
tooltip=_('Base name for this user services'), tooltip=_('Base name for this user services'),
# In this case, the choice can have none value selected by default # In this case, the choice can have none value selected by default
required=True, 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: 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 # so we only need to validate params if values is not None
if values: if values:
if self.colour.value == 'nonsense': 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 # Services itself are non testeable right now, so we don't even have
# to provide one!!! # to provide one!!!
# Congratulations!!!, the needed part of your first simple service is done! # Congratulations!!!, the needed part of your first simple service is done!
# Now you can go to administration panel, and check it # 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 Just a second service, no comments here (almost same that ServiceOne
""" """
typeName = _('Sample Service Two') typeName = _('Sample Service Two')
typeType = 'SampleService2' typeType = 'SampleService2'
typeDescription = _('Sample (and dummy) service ONE+ONE') 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) # : Types of deploys (services in cache and/or assigned to users)
deployedType = SampleUserDeploymentTwo deployedType = SampleUserDeploymentTwo
# Gui, we will use here the EditableList field # Gui, we will use here the EditableList field
names = gui.EditableList(label=_('List of names')) names = gui.EditableList(label=_('List of names'))

View File

@ -48,7 +48,20 @@ if typing.TYPE_CHECKING:
logger = logging.getLogger(__name__) 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' NO_MORE_NAMES = 'NO-NAME-ERROR'
@ -80,16 +93,18 @@ class XenLinkedDeployment(UserDeployment):
# Serializable needed methods # Serializable needed methods
def marshal(self) -> bytes: def marshal(self) -> bytes:
return b'\1'.join([ return b'\1'.join(
b'v1', [
self._name.encode('utf8'), b'v1',
self._ip.encode('utf8'), self._name.encode('utf8'),
self._mac.encode('utf8'), self._ip.encode('utf8'),
self._vmid.encode('utf8'), self._mac.encode('utf8'),
self._reason.encode('utf8'), self._vmid.encode('utf8'),
pickle.dumps(self._queue, protocol=0), self._reason.encode('utf8'),
self._task.encode('utf8') pickle.dumps(self._queue, protocol=0),
]) self._task.encode('utf8'),
]
)
def unmarshal(self, data: bytes) -> None: def unmarshal(self, data: bytes) -> None:
vals = data.split(b'\1') vals = data.split(b'\1')
@ -106,7 +121,9 @@ class XenLinkedDeployment(UserDeployment):
def getName(self) -> str: def getName(self) -> str:
if not self._name: if not self._name:
try: try:
self._name = self.nameGenerator().get(self.service().getBaseName(), self.service().getLenName()) self._name = self.nameGenerator().get(
self.service().getBaseName(), self.service().getLenName()
)
except KeyError: except KeyError:
return NO_MORE_NAMES return NO_MORE_NAMES
return self._name return self._name
@ -168,7 +185,16 @@ class XenLinkedDeployment(UserDeployment):
if forLevel2 is False: if forLevel2 is False:
self._queue = [opCreate, opConfigure, opProvision, opStart, opFinish] self._queue = [opCreate, opConfigure, opProvision, opStart, opFinish]
else: 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: def __getCurrentOp(self) -> int:
if len(self._queue) == 0: if len(self._queue) == 0:
@ -196,7 +222,11 @@ class XenLinkedDeployment(UserDeployment):
if self._vmid != '': # Powers off and delete VM if self._vmid != '': # Powers off and delete VM
try: try:
state = self.service().getVMPowerState(self._vmid) 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().stopVM(self._vmid, False) # In sync mode
self.service().removeVM(self._vmid) self.service().removeVM(self._vmid)
except Exception: except Exception:
@ -226,14 +256,16 @@ class XenLinkedDeployment(UserDeployment):
opWait: self.__wait, opWait: self.__wait,
opRemove: self.__remove, opRemove: self.__remove,
opConfigure: self.__configure, opConfigure: self.__configure,
opProvision: self.__provision opProvision: self.__provision,
} }
try: try:
execFnc: typing.Optional[typing.Callable[[], str]] = fncs.get(op, None) execFnc: typing.Optional[typing.Callable[[], str]] = fncs.get(op, None)
if execFnc is None: if execFnc is None:
return self.__error('Unknown operation found at execution queue ({0})'.format(op)) return self.__error(
'Unknown operation found at execution queue ({0})'.format(op)
)
execFnc() execFnc()
@ -265,9 +297,13 @@ class XenLinkedDeployment(UserDeployment):
templateId = self.publication().getTemplateId() templateId = self.publication().getTemplateId()
name = self.getName() name = self.getName()
if name == NO_MORE_NAMES: if name == NO_MORE_NAMES:
raise Exception('No more names available for this service. (Increase digits for this service to fix)') raise Exception(
'No more names available for this service. (Increase digits for this service to fix)'
)
name = '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' comments = 'UDS Linked clone'
self._task = self.service().startDeployFromTemplate(name, comments, templateId) self._task = self.service().startDeployFromTemplate(name, comments, templateId)
@ -282,7 +318,7 @@ class XenLinkedDeployment(UserDeployment):
""" """
state = self.service().getVMPowerState(self._vmid) 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.__pushFrontOp(opStop)
self.__executeQueue() self.__executeQueue()
else: else:
@ -348,7 +384,9 @@ class XenLinkedDeployment(UserDeployment):
""" """
Makes machine usable on Xen 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 return State.RUNNING
@ -434,14 +472,16 @@ class XenLinkedDeployment(UserDeployment):
opSuspend: self.__checkSuspend, opSuspend: self.__checkSuspend,
opRemove: self.__checkRemoved, opRemove: self.__checkRemoved,
opConfigure: self.__checkConfigure, opConfigure: self.__checkConfigure,
opProvision: self.__checkProvision opProvision: self.__checkProvision,
} }
try: try:
chkFnc: typing.Optional[typing.Callable[[], str]] = fncs.get(op, None) chkFnc: typing.Optional[typing.Callable[[], str]] = fncs.get(op, None)
if chkFnc is None: if chkFnc is None:
return self.__error('Unknown operation found at check queue ({})'.format(op)) return self.__error(
'Unknown operation found at check queue ({})'.format(op)
)
state = chkFnc() state = chkFnc()
if state == State.FINISHED: if state == State.FINISHED:
@ -503,8 +543,16 @@ class XenLinkedDeployment(UserDeployment):
opFinish: 'finish', opFinish: 'finish',
opRetry: 'retry', opRetry: 'retry',
opConfigure: 'configuring', opConfigure: 'configuring',
opProvision: 'provisioning' opProvision: 'provisioning',
}.get(op, '????') }.get(op, '????')
def __debug(self, txt: str) -> None: def __debug(self, txt: str) -> None:
logger.debug('State at %s: name: %s, ip: %s, mac: %s, vmid:%s, queue: %s', txt, self._name, self._ip, self._mac, self._vmid, [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],
)

View File

@ -45,7 +45,9 @@ logger = logging.getLogger(__name__)
class XenPublication(Publication): 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 = '' _name: str = ''
_reason: str = '' _reason: str = ''
@ -61,23 +63,44 @@ class XenPublication(Publication):
""" """
returns data from an instance of Sample Publication serialized returns data from an instance of Sample Publication serialized
""" """
return '\t'.join(['v1', self._name, self._reason, self._destroyAfter, self._templateId, self._state, 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: def unmarshal(self, data: bytes) -> None:
""" """
deserializes the data and loads it inside instance. 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') vals = data.decode('utf8').split('\t')
if vals[0] == 'v1': 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: def publish(self) -> str:
""" """
Realizes the publication of the service Realizes the publication of the service
""" """
self._name = self.service().sanitizeVmName('UDS Pub ' + self.dsName() + "-" + str(self.revision())) self._name = self.service().sanitizeVmName(
comments = _('UDS pub for {0} at {1}').format(self.dsName(), str(datetime.now()).split('.')[0]) '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._reason = '' # No error, no reason for it
self._destroyAfter = 'f' self._destroyAfter = 'f'
self._state = 'ok' self._state = 'ok'

View File

@ -54,6 +54,7 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods
""" """
# : Name to show the administrator. This string will be translated BEFORE # : Name to show the administrator. This string will be translated BEFORE
# : sending it to administration interface, so don't forget to # : sending it to administration interface, so don't forget to
# : mark it as _ (using ugettext_noop) # : mark it as _ (using ugettext_noop)
@ -78,15 +79,13 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods
usesCache = True usesCache = True
# : Tooltip shown to user when this item is pointed at admin interface, none # : Tooltip shown to user when this item is pointed at admin interface, none
# : because we don't use it # : because we don't use it
cacheTooltip = _( cacheTooltip = _('Number of desired machines to keep running waiting for a user')
'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 # : If we need to generate a "Level 2" cache for this service (i.e., L1
# : could be running machines and L2 suspended machines) # : could be running machines and L2 suspended machines)
usesCache_L2 = True usesCache_L2 = True
# : Tooltip shown to user when this item is pointed at admin interface, None # : Tooltip shown to user when this item is pointed at admin interface, None
# : also because we don't use it # : also because we don't use it
cacheTooltip_L2 = _( cacheTooltip_L2 = _('Number of desired machines to keep suspended waiting for use')
'Number of desired machines to keep suspended waiting for use')
# : If the service needs a s.o. manager (managers are related to agents # : If the service needs a s.o. manager (managers are related to agents
# : provided by services itselfs, i.e. virtual machines with actors) # : 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"), label=_("Storage SR"),
rdonly=False, rdonly=False,
order=100, order=100,
tooltip=_('Storage where to publish and put incrementals (only shared storages are supported)'), tooltip=_(
required=True 'Storage where to publish and put incrementals (only shared storages are supported)'
),
required=True,
) )
minSpaceGB = gui.NumericField( minSpaceGB = gui.NumericField(
@ -119,7 +120,7 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods
defvalue='32', defvalue='32',
order=101, order=101,
tooltip=_('Minimal free space in GB'), tooltip=_('Minimal free space in GB'),
required=True required=True,
) )
machine = gui.ChoiceField( machine = gui.ChoiceField(
@ -127,7 +128,7 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods
order=110, order=110,
tooltip=_('Service base machine'), tooltip=_('Service base machine'),
tab=_('Machine'), tab=_('Machine'),
required=True required=True,
) )
network = gui.ChoiceField( network = gui.ChoiceField(
@ -136,17 +137,18 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods
order=111, order=111,
tooltip=_('Network used for virtual machines'), tooltip=_('Network used for virtual machines'),
tab=_('Machine'), tab=_('Machine'),
required=True required=True,
) )
memory = gui.NumericField( memory = gui.NumericField(
label=_("Memory (Mb)"), label=_("Memory (Mb)"),
length=4, defvalue=512, length=4,
defvalue=512,
rdonly=False, rdonly=False,
order=112, order=112,
tooltip=_('Memory assigned to machines'), tooltip=_('Memory assigned to machines'),
tab=_('Machine'), tab=_('Machine'),
required=True required=True,
) )
shadow = gui.NumericField( shadow = gui.NumericField(
@ -157,7 +159,7 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods
order=113, order=113,
tooltip=_('Shadow memory multiplier (use with care)'), tooltip=_('Shadow memory multiplier (use with care)'),
tab=_('Machine'), tab=_('Machine'),
required=True required=True,
) )
baseName = gui.TextField( baseName = gui.TextField(
@ -166,7 +168,7 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods
order=114, order=114,
tooltip=_('Base name for clones from this machine'), tooltip=_('Base name for clones from this machine'),
tab=_('Machine'), tab=_('Machine'),
required=True required=True,
) )
lenName = gui.NumericField( lenName = gui.NumericField(
@ -176,7 +178,7 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods
order=115, order=115,
tooltip=_('Size of numeric part for the names of these machines'), tooltip=_('Size of numeric part for the names of these machines'),
tab=_('Machine'), tab=_('Machine'),
required=True required=True,
) )
def initialize(self, values): def initialize(self, values):
@ -191,7 +193,8 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods
if int(self.memory.value) < 256: if int(self.memory.value) < 256:
raise Service.ValidationException( raise Service.ValidationException(
_('The minimum allowed memory is 256 Mb')) _('The minimum allowed memory is 256 Mb')
)
def parent(self) -> 'XenProvider': def parent(self) -> 'XenProvider':
return typing.cast('XenProvider', super().parent()) 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 # This is that value is always '', so if we want to change something, we have to do it
# at defValue # 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 = [] storages_list = []
for storage in self.parent().getStorages(): for storage in self.parent().getStorages():
space, free = storage['size'] / 1024, (storage['size'] - storage['used']) / 1024 space, free = (
storages_list.append(gui.choiceItem(storage['id'], "%s (%4.2f Gb/%4.2f Gb)" % (storage['name'], 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.machine.setValues(machines_list)
self.datastore.setValues(storages_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) logger.debug('Checking datastore space for %s: %s', self.datastore.value, info)
availableGB = (info['size'] - info['used']) / 1024 availableGB = (info['size'] - info['used']) / 1024
if availableGB < self.minSpaceGB.num(): 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: 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. 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 # Checks datastore available space, raises exeception in no min available
self.datastoreHasSpace() 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: def convertToTemplate(self, machineId: str) -> None:
""" """

View File

@ -69,8 +69,10 @@ translation = gettext.translation('xen-xm', fallback=True)
API_VERSION_1_1 = '1.1' API_VERSION_1_1 = '1.1'
API_VERSION_1_2 = '1.2' API_VERSION_1_2 = '1.2'
class Failure(Exception): class Failure(Exception):
details: typing.List[typing.Any] details: typing.List[typing.Any]
def __init__(self, details: typing.List[typing.Any]): def __init__(self, details: typing.List[typing.Any]):
super().__init__() super().__init__()
self.details = details self.details = details
@ -87,19 +89,24 @@ class Failure(Exception):
# dict([(str(i), self.details[i]) for i in range(len(self.details))]) # dict([(str(i), self.details[i]) for i in range(len(self.details))])
return {str(i): d for i, d in enumerate(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 # Just a "constant" that we use to decide whether to retry the RPC
_RECONNECT_AND_RETRY = object() _RECONNECT_AND_RETRY = object()
class UDSHTTPConnection(httplib.HTTPConnection): 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): def connect(self):
path = self.host.replace("_", "/") path = self.host.replace("_", "/")
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.sock.connect(path) self.sock.connect(path)
class UDSHTTP(httplib.HTTPConnection): class UDSHTTP(httplib.HTTPConnection):
_connection_class = UDSHTTPConnection _connection_class = UDSHTTPConnection
class UDSTransport(xmlrpclib.Transport): class UDSTransport(xmlrpclib.Transport):
_use_datetime: bool _use_datetime: bool
_extra_headers: typing.List[typing.Tuple[str, str]] _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 def make_connection(self, host: str) -> httplib.HTTPConnection: # type: ignore # In our case, host is always an string
return UDSHTTPConnection(host) 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) connection.putrequest("POST", handler)
for key, value in self._extra_headers: for key, value in self._extra_headers:
connection.putheader(key, value) connection.putheader(key, value)
class Session(xmlrpclib.ServerProxy): class Session(xmlrpclib.ServerProxy):
"""A server proxy and session manager for communicating with xapi using """A server proxy and session manager for communicating with xapi using
the Xen-API. the Xen-API.
@ -134,25 +144,40 @@ class Session(xmlrpclib.ServerProxy):
session.xenapi.session.logout() session.xenapi.session.logout()
""" """
def __init__(self, uri, transport=None, encoding=None, verbose=0, def __init__(
allow_none=1, ignore_ssl=False): self,
uri,
transport=None,
encoding=None,
verbose=0,
allow_none=1,
ignore_ssl=False,
):
# Fix for CA-172901 (+ Python 2.4 compatibility) # Fix for CA-172901 (+ Python 2.4 compatibility)
# Fix for context=ctx ( < Python 2.7.9 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() ctx = ssl._create_unverified_context()
xmlrpclib.ServerProxy.__init__(self, uri, transport, encoding, xmlrpclib.ServerProxy.__init__(
verbose, allow_none, context=ctx) self, uri, transport, encoding, verbose, allow_none, context=ctx
)
else: else:
xmlrpclib.ServerProxy.__init__(self, uri, transport, encoding, xmlrpclib.ServerProxy.__init__(
verbose, allow_none) self, uri, transport, encoding, verbose, allow_none
)
self.transport = transport self.transport = transport
self._session = None self._session = None
self.last_login_method = None self.last_login_method = None
self.last_login_params = None self.last_login_params = None
self.API_version = API_VERSION_1_1 self.API_version = API_VERSION_1_1
def xenapi_request(self, methodname, params): def xenapi_request(self, methodname, params):
if methodname.startswith('login'): if methodname.startswith('login'):
self._login(methodname, params) self._login(methodname, params)
@ -168,22 +193,20 @@ class Session(xmlrpclib.ServerProxy):
if result is _RECONNECT_AND_RETRY: if result is _RECONNECT_AND_RETRY:
retry_count += 1 retry_count += 1
if self.last_login_method: if self.last_login_method:
self._login(self.last_login_method, self._login(self.last_login_method, self.last_login_params)
self.last_login_params)
else: else:
raise xmlrpclib.Fault(401, 'You must log in') raise xmlrpclib.Fault(401, 'You must log in')
else: else:
return result return result
raise xmlrpclib.Fault( 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): def _login(self, method, params):
try: try:
result = _parse_result( result = _parse_result(getattr(self, 'session.%s' % method)(*params))
getattr(self, 'session.%s' % method)(*params))
if result is _RECONNECT_AND_RETRY: if result is _RECONNECT_AND_RETRY:
raise xmlrpclib.Fault( raise xmlrpclib.Fault(500, 'Received SESSION_INVALID when logging in')
500, 'Received SESSION_INVALID when logging in')
self._session = result self._session = result
self.last_login_method = method self.last_login_method = method
self.last_login_params = params self.last_login_params = params
@ -195,7 +218,7 @@ class Session(xmlrpclib.ServerProxy):
def _logout(self): def _logout(self):
try: 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 return _parse_result(self.session.local_logout(self._session)) # type: ignore
else: else:
return _parse_result(self.session.logout(self._session)) # type: ignore 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) host = self.xenapi.pool.get_master(pool)
major = self.xenapi.host.get_API_version_major(host) major = self.xenapi.host.get_API_version_major(host)
minor = self.xenapi.host.get_API_version_minor(host) minor = self.xenapi.host.get_API_version_minor(host)
return "%s.%s"%(major, minor) return "%s.%s" % (major, minor)
def __getattr__(self, name): def __getattr__(self, name):
if name == 'handle': if name == 'handle':
@ -224,12 +247,16 @@ class Session(xmlrpclib.ServerProxy):
else: else:
return xmlrpclib.ServerProxy.__getattr__(self, name) return xmlrpclib.ServerProxy.__getattr__(self, name)
def xapi_local(): def xapi_local():
return Session("http://_var_lib_xcp_xapi/", transport=UDSTransport()) return Session("http://_var_lib_xcp_xapi/", transport=UDSTransport())
def _parse_result(result): def _parse_result(result):
if not isinstance(result, dict) or 'Status' not in 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 result['Status'] == 'Success':
if 'Value' in result: if 'Value' in result:
return result['Value'] return result['Value']
@ -258,7 +285,9 @@ class _Dispatcher:
if self.__name is None: if self.__name is None:
return _Dispatcher(self.__API_version, self.__send, name) return _Dispatcher(self.__API_version, self.__send, name)
else: 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): def __call__(self, *args):
return self.__send(self.__name, args) return self.__send(self.__name, args)

View File

@ -38,6 +38,7 @@ logger = logging.getLogger(__name__)
TAG_TEMPLATE = "uds-template" TAG_TEMPLATE = "uds-template"
TAG_MACHINE = "uds-machine" TAG_MACHINE = "uds-machine"
class XenFault(Exception): class XenFault(Exception):
pass pass
@ -113,7 +114,16 @@ class XenServer: # pylint: disable=too-many-public-methods
_poolName: str _poolName: str
_apiVersion: 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._originalHost = self._host = host
self._host_backup = host_backup or '' self._host_backup = host_backup or ''
self._port = str(port) self._port = str(port)
@ -186,7 +196,11 @@ class XenServer: # pylint: disable=too-many-public-methods
self._poolName = str(self.getPoolName()) 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... 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': 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._host = e.details[1]
self.login() self.login()
else: else:
@ -246,7 +260,11 @@ class XenServer: # pylint: disable=too-many-public-methods
status = 'failure' status = 'failure'
# Removes <value></value> if present # Removes <value></value> if present
if result and not isinstance(result, XenFailure) and result.startswith('<value>'): if (
result
and not isinstance(result, XenFailure)
and result.startswith('<value>')
):
result = result[7:-8] result = result[7:-8]
if destroyTask: if destroyTask:
@ -255,14 +273,18 @@ class XenServer: # pylint: disable=too-many-public-methods
except Exception as e: except Exception as e:
logger.warning('Destroy task %s returned error %s', task, str(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]]: def getSRs(self) -> typing.Iterable[typing.MutableMapping[str, typing.Any]]:
for srId in self.SR.get_all(): for srId in self.SR.get_all():
# Only valid SR shared, non iso # Only valid SR shared, non iso
name_label = self.SR.get_name_label(srId) name_label = self.SR.get_name_label(srId)
# Skip non valid... # 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 continue
valid = True valid = True
@ -276,7 +298,7 @@ class XenServer: # pylint: disable=too-many-public-methods
'id': srId, 'id': srId,
'name': name_label, 'name': name_label,
'size': XenServer.toMb(self.SR.get_physical_size(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 getSRInfo(self, srId: str) -> typing.MutableMapping[str, typing.Any]: def getSRInfo(self, srId: str) -> typing.MutableMapping[str, typing.Any]:
@ -284,22 +306,24 @@ class XenServer: # pylint: disable=too-many-public-methods
'id': srId, 'id': srId,
'name': self.SR.get_name_label(srId), 'name': self.SR.get_name_label(srId),
'size': XenServer.toMb(self.SR.get_physical_size(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]]: def getNetworks(self) -> typing.Iterable[typing.MutableMapping[str, typing.Any]]:
for netId in self.network.get_all(): 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 { yield {
'id': netId, 'id': netId,
'name': self.network.get_name_label(netId), 'name': self.network.get_name_label(netId),
} }
def getNetworkInfo(self, netId: str) -> typing.MutableMapping[str, typing.Any]: def getNetworkInfo(self, netId: str) -> typing.MutableMapping[str, typing.Any]:
return { return {'id': netId, 'name': self.network.get_name_label(netId)}
'id': netId,
'name': self.network.get_name_label(netId)
}
def getVMs(self) -> typing.Iterable[typing.MutableMapping[str, typing.Any]]: def getVMs(self) -> typing.Iterable[typing.MutableMapping[str, typing.Any]]:
try: 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.. # if self.VM.get_is_a_template(vm): # Sample set_tags, easy..
# self.VM.set_tags(vm, ['template']) # self.VM.set_tags(vm, ['template'])
# continue # continue
if self.VM.get_is_control_domain(vm) or \ if self.VM.get_is_control_domain(vm) or self.VM.get_is_a_template(vm):
self.VM.get_is_a_template(vm):
continue continue
yield {'id': vm, 'name': self.VM.get_name_label(vm)} 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.Async.VM.resume(vmId, False, False)
return self.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: If targetSR is NONE:
Clones the specified VM, making a new VM. Clones the specified VM, making a new VM.
@ -402,11 +427,15 @@ class XenServer: # pylint: disable=too-many-public-methods
try: try:
if targetSR: if targetSR:
if 'copy' not in operations: 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) task = self.Async.VM.copy(vmId, targetName, targetSR)
else: else:
if 'clone' not in operations: 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) task = self.Async.VM.clone(vmId, targetName)
return task return task
except XenAPI.Failure as e: except XenAPI.Failure as e:
@ -490,7 +519,9 @@ class XenServer: # pylint: disable=too-many-public-methods
operations = self.VM.get_allowed_operations(vmId) operations = self.VM.get_allowed_operations(vmId)
logger.debug('Allowed operations: %s', operations) logger.debug('Allowed operations: %s', operations)
if 'make_into_template' not in 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) self.VM.set_is_a_template(vmId, True)
# Apply that is an "UDS Template" taggint it # Apply that is an "UDS Template" taggint it

View File

@ -63,7 +63,7 @@ def __init__():
for _, name, _ in pkgutil.iter_modules([pkgpath]): for _, name, _ in pkgutil.iter_modules([pkgpath]):
# __import__('uds.services.' + name, globals(), locals(), []) # __import__('uds.services.' + name, globals(), locals(), [])
importlib.import_module('.' + name, __name__) # import module importlib.import_module('.' + name, __name__) # import module
importlib.invalidate_caches() importlib.invalidate_caches()
for p in [services.ServiceProvider]: for p in [services.ServiceProvider]: