sample provider moved to 3.7

This commit is contained in:
Adolfo Gómez García 2019-10-02 10:51:43 +02:00
parent d48e71cfd3
commit a32bb87a78
4 changed files with 81 additions and 98 deletions

View File

@ -90,7 +90,7 @@ class SampleUserDeploymentOne(services.UserDeployment):
Does nothing here also, all data are kept at environment storage
"""
def getName(self):
def getName(self) -> str:
"""
We override this to return a name to display. Default implementation
(in base class), returns getUniqueIde() value
@ -111,16 +111,15 @@ class SampleUserDeploymentOne(services.UserDeployment):
a new unique name, so we keep the first generated name cached and don't
generate more names. (Generator are simple utility classes)
"""
name = self.storage.readData('name')
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)
return name
def setIp(self, ip):
def setIp(self, ip: str) -> None:
"""
In our case, there is no OS manager associated with this, so this method
will never get called, but we put here as sample.
@ -132,9 +131,9 @@ class SampleUserDeploymentOne(services.UserDeployment):
:note: This IP is the IP of the "consumed service", so the transport can
access it.
"""
self.storage.saveData('ip', str(ip))
self.storage.saveData('ip', ip)
def getUniqueId(self):
def getUniqueId(self) -> str:
"""
Return and unique identifier for this service.
In our case, we will generate a mac name, that can be also as sample
@ -144,13 +143,13 @@ class SampleUserDeploymentOne(services.UserDeployment):
The get method of a mac generator takes one param, that is the mac range
to use to get an unused mac.
"""
mac = self.storage.readData('mac')
mac = typing.cast(str, self.storage.readData('mac'))
if mac is None:
mac = self.macGenerator().get('00:00:00:00:00:00-00:FF:FF:FF:FF:FF')
self.storage.saveData('mac', mac)
return mac
def getIp(self):
def getIp(self) -> str:
"""
We need to implement this method, so we can return the IP for transports
use. If no IP is known for this service, this must return None
@ -168,12 +167,12 @@ class SampleUserDeploymentOne(services.UserDeployment):
show the IP to the administrator, this method will get called
"""
ip = self.storage.readData('ip')
ip = typing.cast(str, self.storage.readData('ip'))
if ip is None:
ip = '192.168.0.34' # Sample IP for testing purposses only
return ip
def setReady(self):
def setReady(self) -> str:
"""
This is a task method. As that, the expected return values are
State values RUNNING, FINISHED or ERROR.
@ -206,7 +205,7 @@ class SampleUserDeploymentOne(services.UserDeployment):
# In our case, the service is always ready
return State.FINISHED
def deployForUser(self, user):
def deployForUser(self, user: 'models.User') -> str:
"""
Deploys an service instance for an user.
@ -243,7 +242,7 @@ class SampleUserDeploymentOne(services.UserDeployment):
return State.RUNNING
def checkState(self):
def checkState(self) -> str:
"""
Our deployForUser method will initiate the consumable service deployment,
but will not finish it.
@ -265,8 +264,10 @@ class SampleUserDeploymentOne(services.UserDeployment):
destroying, and cancel will simply invoke destroy
"""
import random
count = int(self.storage.readData('count')) + 1
countStr: typing.Optional[str] = typing.cast(str, self.storage.readData('count'))
count: int = 0
if countStr:
count = int(countStr) + 1
# Count is always a valid value, because this method will never get
# called before deployForUser, deployForCache, destroy or cancel.
# In our sample, we only use checkState in case of deployForUser,
@ -282,7 +283,7 @@ class SampleUserDeploymentOne(services.UserDeployment):
self.storage.saveData('count', str(count))
return State.RUNNING
def finish(self):
def finish(self) -> None:
"""
Invoked when the core notices that the deployment of a service has finished.
(No matter wether it is for cache or for an user)
@ -295,7 +296,7 @@ class SampleUserDeploymentOne(services.UserDeployment):
# Note that this is not really needed, is just a sample of storage use
self.storage.remove('count')
def userLoggedIn(self, user):
def userLoggedIn(self, username: str) -> None:
"""
This method must be available so os managers can invoke it whenever
an user get logged into a service.
@ -310,9 +311,9 @@ class SampleUserDeploymentOne(services.UserDeployment):
The user provided is just an string, that is provided by actor.
"""
# We store the value at storage, but never get used, just an example
self.storage.saveData('user', user)
self.storage.saveData('user', username)
def userLoggedOut(self, user):
def userLoggedOut(self, username) -> None:
"""
This method must be available so os managers can invoke it whenever
an user get logged out if a service.
@ -329,7 +330,7 @@ class SampleUserDeploymentOne(services.UserDeployment):
# We do nothing more that remove the user
self.storage.remove('user')
def reasonOfError(self):
def reasonOfError(self) -> str:
"""
Returns the reason of the error.
@ -337,9 +338,9 @@ class SampleUserDeploymentOne(services.UserDeployment):
for it, and it will be asked everytime it's needed to be shown to the
user (when the administation asks for it).
"""
return self.storage.readData('error') or 'No error'
return typing.cast(str, self.storage.readData('error')) or 'No error'
def destroy(self):
def destroy(self) -> str:
"""
This is a task method. As that, the excepted return values are
State values RUNNING, FINISHED or ERROR.
@ -350,7 +351,7 @@ class SampleUserDeploymentOne(services.UserDeployment):
"""
return State.FINISHED
def cancel(self):
def cancel(self) -> str:
"""
This is a task method. As that, the excepted return values are
State values RUNNING, FINISHED or ERROR.

View File

@ -77,7 +77,23 @@ class SampleUserDeploymentTwo(services.UserDeployment):
# : Recheck every five seconds by default (for task methods)
suggestedTime = 2
def initialize(self):
_name: str
_ip: str
_mac: str
_error: str
_count: int
# Utility overrides for type checking...
def service(self) -> 'ServiceOne':
return typing.cast('ServiceOne', super().service())
def publication(self) -> 'SamplePublication':
pub = super().publication()
if pub is None:
raise Exception('No publication for this element!')
return typing.cast('SamplePublication', pub)
def initialize(self) -> None:
"""
Initialize default attributes values here. We can do whatever we like,
but for this sample this is just right...
@ -89,7 +105,7 @@ class SampleUserDeploymentTwo(services.UserDeployment):
self._count = 0
# Serializable needed methods
def marshal(self):
def marshal(self) -> bytes:
"""
Marshal own data, in this sample we will marshal internal needed
attributes.
@ -105,21 +121,21 @@ class SampleUserDeploymentTwo(services.UserDeployment):
"""
data = '\t'.join(['v1', self._name, self._ip, self._mac, self._error,
str(self._count)])
return codecs.encode(data.encode('utf8'), 'zip')
return codecs.encode(data.encode(), encoding='zip') # type: ignore
def unmarshal(self, data):
def unmarshal(self, data: bytes) -> None:
"""
We unmarshal the content.
"""
data = codecs.decode(data, 'zip').decode('utf8').split('\t')
values: typing.List[str] = codecs.decode(data, 'zip').decode().split('\t') # type: ignore
# Data Version check
# If we include some new data at some point in a future, we can
# add "default" values at v1 check, and load new values at 'v2' check.
if data[0] == 'v1':
self._name, self._ip, self._mac, self._error, count = data[1:]
if values[0] == 'v1':
self._name, self._ip, self._mac, self._error, count = values[1:]
self._count = int(count)
def getName(self):
def getName(self) -> str:
"""
We override this to return a name to display. Default implementation
(in base class), returns getUniqueIde() value
@ -141,12 +157,11 @@ class SampleUserDeploymentTwo(services.UserDeployment):
generate more names. (Generator are simple utility classes)
"""
if self._name == '':
self._name = self.nameGenerator().get(self.publication().getBaseName(),
3)
self._name = self.nameGenerator().get(self.publication().getBaseName(), 3)
# self._name will be stored when object is marshaled
return self._name
def setIp(self, ip):
def setIp(self, ip: str) -> None:
"""
In our case, there is no OS manager associated with this, so this method
will never get called, but we put here as sample.
@ -160,7 +175,7 @@ class SampleUserDeploymentTwo(services.UserDeployment):
"""
self._ip = ip
def getUniqueId(self):
def getUniqueId(self) -> str:
"""
Return and unique identifier for this service.
In our case, we will generate a mac name, that can be also as sample
@ -188,7 +203,7 @@ class SampleUserDeploymentTwo(services.UserDeployment):
self._mac = self.macGenerator().get('00:00:00:00:00:00-00:FF:FF:FF:FF:FF')
return self._mac
def getIp(self):
def getIp(self) -> str:
"""
We need to implement this method, so we can return the IP for transports
use. If no IP is known for this service, this must return None
@ -210,7 +225,7 @@ class SampleUserDeploymentTwo(services.UserDeployment):
return '192.168.0.34' # Sample IP for testing purposes only
return self._ip
def setReady(self):
def setReady(self) -> str:
"""
This is a task method. As that, the excepted return values are
State values RUNNING, FINISHED or ERROR.
@ -243,7 +258,7 @@ class SampleUserDeploymentTwo(services.UserDeployment):
# In our case, the service is always ready
return State.FINISHED
def deployForUser(self, user):
def deployForUser(self, user: 'models.User') -> str:
"""
Deploys an service instance for an user.
@ -282,7 +297,7 @@ class SampleUserDeploymentTwo(services.UserDeployment):
return State.RUNNING
def deployForCache(self, cacheLevel):
def deployForCache(self, cacheLevel: int) -> str:
"""
Deploys a user deployment as cache.
@ -301,37 +316,7 @@ class SampleUserDeploymentTwo(services.UserDeployment):
self._count = 0
return State.RUNNING
def moveToCache(self, newLevel):
"""
This method is invoked whenever the core needs to move from the current
cache level to a new cache level an user deployment.
This is a task method. As that, the expected return values are
State values RUNNING, FINISHED or ERROR.
We only provide newLevel, because there is only two cache levels, so if
newLevel is L1, the actual is L2, and if it is L2, the actual is L1.
Actually there is no possibility to move assigned services again back to
cache. If some service needs that kind of functionallity, this must be
provided at service level (for example, when doing publishing creating
a number of services that will be used, released and reused by users).
Also, user deployments that are at cache level 2 will never get directly
assigned to user. First, it will pass to L1 and then it will get assigned.
A good sample of a real implementation of this is moving a virtual machine
from a "suspended" state to "running" state to assign it to an user.
In this sample, there is L2 cache also, but moving from L1 to L2 and
from L2 to L1 is doing really nothing, so this method will do nothing.
In a real scenario, we will, for example, suspend or resume virtual machine
and, return State.RUNNING and at checkState check if this task is completed.
"""
pass
def checkState(self):
def checkState(self) -> str:
"""
Our deployForUser method will initiate the consumable service deployment,
but will not finish it.
@ -386,7 +371,7 @@ class SampleUserDeploymentTwo(services.UserDeployment):
# We set count to 0, not needed but for sample purposes
self._count = 0
def userLoggedIn(self, user):
def userLoggedIn(self, username: str) -> None:
"""
This method must be available so os managers can invoke it whenever
an user get logged into a service.
@ -401,9 +386,9 @@ class SampleUserDeploymentTwo(services.UserDeployment):
The user provided is just an string, that is provided by actors.
"""
# We store the value at storage, but never get used, just an example
self.storage.saveData('user', user)
self.storage.saveData('user', username)
def userLoggedOut(self, user):
def userLoggedOut(self, username) -> None:
"""
This method must be available so os managers can invoke it whenever
an user get logged out if a service.
@ -420,7 +405,7 @@ class SampleUserDeploymentTwo(services.UserDeployment):
# We do nothing more that remove the user
self.storage.remove('user')
def reasonOfError(self):
def reasonOfError(self) -> str:
"""
Returns the reason of the error.
@ -435,7 +420,7 @@ class SampleUserDeploymentTwo(services.UserDeployment):
"""
return self._error
def destroy(self):
def destroy(self) -> str:
"""
This is a task method. As that, the excepted return values are
State values RUNNING, FINISHED or ERROR.
@ -446,7 +431,7 @@ class SampleUserDeploymentTwo(services.UserDeployment):
"""
return State.FINISHED
def cancel(self):
def cancel(self) -> str:
"""
This is a task method. As that, the excepted return values are
State values RUNNING, FINISHED or ERROR.

View File

@ -34,7 +34,7 @@ import logging
import typing
from django.utils.translation import ugettext as _
from uds.core.services import Publication
from uds.core import services
from uds.core.util.state import State
logger = logging.getLogger(__name__)
@ -44,7 +44,7 @@ if typing.TYPE_CHECKING:
from .service import ServiceOne, ServiceTwo
class SamplePublication(Publication):
class SamplePublication(services.Publication):
"""
This class shows how a publication is developed.
@ -87,7 +87,7 @@ class SamplePublication(Publication):
_reason: str = ''
_number: int = -1
def initialize(self):
def initialize(self) -> None:
"""
This method will be invoked by default __init__ of base class, so it gives
us the oportunity to initialize whataver we need here.
@ -101,13 +101,13 @@ class SamplePublication(Publication):
self._reason = '' # No error, no reason for it
self._number = 1
def marshal(self):
def marshal(self) -> bytes:
"""
returns data from an instance of Sample Publication serialized
"""
return '\t'.join([self._name, self._reason, str(self._number)]).encode('utf8')
def unmarshal(self, data):
def unmarshal(self, data: bytes) -> None:
"""
deserializes the data and loads it inside instance.
"""
@ -118,7 +118,7 @@ class SamplePublication(Publication):
self._reason = vals[1]
self._number = int(vals[2])
def publish(self):
def publish(self) -> str:
"""
This method is invoked whenever the administrator requests a new publication.
@ -175,7 +175,7 @@ class SamplePublication(Publication):
self._reason = ''
return State.RUNNING
def checkState(self):
def checkState(self) -> str:
"""
Our publish method will initiate publication, but will not finish it.
So in our sample, wi will only check if _number reaches 0, and if so
@ -204,10 +204,9 @@ class SamplePublication(Publication):
if self._number <= 0:
return State.FINISHED
else:
return State.RUNNING
return State.RUNNING
def finish(self):
def finish(self) -> None:
"""
Invoked when Publication manager noticed that the publication has finished.
This give us the oportunity of cleaning up things (as stored vars, etc..),
@ -221,7 +220,7 @@ class SamplePublication(Publication):
# Make simply a random string
self._name = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(10))
def reasonOfError(self):
def reasonOfError(self) -> str:
"""
If a publication produces an error, here we must notify the reason why
it happened. This will be called just after publish or checkState
@ -231,7 +230,7 @@ class SamplePublication(Publication):
"""
return self._reason
def destroy(self):
def destroy(self) -> str:
"""
This is called once a publication is no more needed.
@ -248,7 +247,7 @@ class SamplePublication(Publication):
# We do not do anything else to destroy this instance of publication
return State.FINISHED
def cancel(self):
def cancel(self) -> str:
"""
Invoked for canceling the current operation.
This can be invoked directly by an administration or by the clean up
@ -268,7 +267,7 @@ class SamplePublication(Publication):
# Methods provided below are specific for this publication
# and will be used by user deployments that uses this kind of publication
def getBaseName(self):
def getBaseName(self) -> str:
"""
This sample method (just for this sample publication), provides
the name generater for this publication. This is just a sample, and

View File

@ -152,7 +152,7 @@ class ServiceOne(services.Service):
defvalue='' # Default value is the ID of the choicefield
)
def initialize(self, values):
def initialize(self, values: 'Module.ValuesType') -> None:
"""
We check here form values to see if they are valid.
@ -163,7 +163,7 @@ class ServiceOne(services.Service):
# We don't need to check anything, bat because this is a sample, we do
# As in provider, we receive values only at new Service creation,
# so we only need to validate params if values is not None
if values is not None:
if values:
if self.colour.value == 'nonsense':
raise services.Service.ValidationException('The selected colour is invalid!!!')
@ -178,7 +178,7 @@ class ServiceOne(services.Service):
# From now onwards, we implement our own methods, that will be used by,
# for example, services derived from this provider
def getColour(self):
def getColour(self) -> str:
"""
Simply returns colour, for deployed user services.
@ -186,15 +186,13 @@ class ServiceOne(services.Service):
"""
return self.colour.value
def getPassw(self):
def getPassw(self) -> str:
"""
Simply returns passwd, for deloyed user services
"""
return self.passw.value
def getBaseName(self):
"""
"""
def getBaseName(self) -> str:
return self.baseName.value
@ -239,7 +237,7 @@ class ServiceTwo(services.Service):
# No checks here
def getNames(self):
def getNames(self) -> str:
"""
For using at deployed services, really nothing
"""