forked from shaba/openuds
More formating
This commit is contained in:
parent
1b7076e645
commit
c72bcf4200
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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)]
|
||||
|
@ -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:
|
||||
"""
|
||||
|
@ -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'))
|
||||
|
||||
|
@ -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],
|
||||
)
|
||||
|
@ -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'
|
||||
|
@ -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:
|
||||
"""
|
||||
|
@ -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)
|
||||
|
@ -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 <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]
|
||||
|
||||
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
|
||||
|
@ -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]:
|
||||
|
Loading…
Reference in New Issue
Block a user