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'))
|
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
|
||||||
|
@ -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:
|
||||||
|
@ -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)]
|
||||||
|
@ -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:
|
||||||
"""
|
"""
|
||||||
|
@ -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'))
|
||||||
|
|
||||||
|
@ -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],
|
||||||
|
)
|
||||||
|
@ -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'
|
||||||
|
@ -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:
|
||||||
"""
|
"""
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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]:
|
||||||
|
Loading…
Reference in New Issue
Block a user