mirror of
https://github.com/dkmstr/openuds.git
synced 2025-01-22 22:03:54 +03:00
Added initial fixed service to OpenStack. Need to complete related tests
This commit is contained in:
parent
c64a788523
commit
7be200f173
@ -134,7 +134,7 @@ class OpenStackLiveUserService(
|
||||
def get_name(self) -> str:
|
||||
if self._name == '':
|
||||
try:
|
||||
self._name = self.name_generator().get(
|
||||
self._name = 'UDS-U-' + self.name_generator().get(
|
||||
self.service().get_basename(), self.service().get_lenname()
|
||||
)
|
||||
except KeyError:
|
||||
@ -278,7 +278,7 @@ class OpenStackLiveUserService(
|
||||
self.do_log(
|
||||
log.LogLevel.INFO, 'Keep on error is enabled, machine will not be marked for deletion'
|
||||
)
|
||||
# Simple fix queue to FINISH and return it
|
||||
# Fix queue to FINISH and return it
|
||||
self._queue = [Operation.FINISH]
|
||||
return types.states.TaskState.FINISHED
|
||||
|
||||
|
108
server/src/uds/services/OpenStack/deployment_fixed.py
Normal file
108
server/src/uds/services/OpenStack/deployment_fixed.py
Normal file
@ -0,0 +1,108 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import logging
|
||||
import typing
|
||||
|
||||
from uds.core import types
|
||||
from uds.core.services.specializations.fixed_machine.fixed_userservice import FixedUserService, Operation
|
||||
from uds.core.util import autoserializable
|
||||
|
||||
from .openstack import types as openstack_types
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
from . import service_fixed
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OpenStackUserServiceFixed(FixedUserService, autoserializable.AutoSerializable):
|
||||
"""
|
||||
This class generates the user consumable elements of the service tree.
|
||||
|
||||
After creating at administration interface an Deployed Service, UDS will
|
||||
create consumable services for users using UserDeployment class as
|
||||
provider of this elements.
|
||||
|
||||
The logic for managing vmware deployments (user machines in this case) is here.
|
||||
|
||||
"""
|
||||
|
||||
# : Recheck every ten seconds by default (for task methods)
|
||||
suggested_delay = 4
|
||||
|
||||
# Utility overrides for type checking...
|
||||
def service(self) -> 'service_fixed.OpenStackServiceFixed':
|
||||
return typing.cast('service_fixed.OpenStackServiceFixed', super().service())
|
||||
|
||||
def set_ready(self) -> types.states.TaskState:
|
||||
if self.cache.get('ready') == '1':
|
||||
return types.states.TaskState.FINISHED
|
||||
|
||||
try:
|
||||
vminfo = self.service().get_machine_info(self._vmid)
|
||||
except Exception as e:
|
||||
return self._error(f'Machine not found: {e}')
|
||||
|
||||
if vminfo.status == 'stopped':
|
||||
self._queue = [Operation.START, Operation.FINISH]
|
||||
return self._execute_queue()
|
||||
|
||||
self.cache.put('ready', '1')
|
||||
return types.states.TaskState.FINISHED
|
||||
|
||||
def reset(self) -> None:
|
||||
"""
|
||||
OpenStack, reset operation
|
||||
"""
|
||||
if self._vmid != '':
|
||||
try:
|
||||
self.service().api.reset_server(self._vmid)
|
||||
except Exception: # nosec: if cannot reset, ignore it
|
||||
pass # If could not reset, ignore it...
|
||||
|
||||
def process_ready_from_os_manager(self, data: typing.Any) -> types.states.TaskState:
|
||||
return types.states.TaskState.FINISHED
|
||||
|
||||
def error(self, reason: str) -> types.states.TaskState:
|
||||
return self._error(reason)
|
||||
|
||||
def _start_machine(self) -> None:
|
||||
try:
|
||||
vminfo = self.service().get_machine_info(self._vmid)
|
||||
except Exception as e:
|
||||
raise Exception('Machine not found on start machine') from e
|
||||
|
||||
if vminfo.power_state != openstack_types.PowerState.RUNNING:
|
||||
self.service().api.start_server(self._vmid) # Start the server
|
||||
|
@ -43,7 +43,7 @@ from .openstack import openstack_client
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def getApi(parameters: dict[str, str]) -> tuple[openstack_client.OpenstackClient, bool]:
|
||||
def get_api(parameters: dict[str, str]) -> tuple[openstack_client.OpenstackClient, bool]:
|
||||
from .provider_legacy import OpenStackProviderLegacy
|
||||
from .provider import OpenStackProvider
|
||||
|
||||
@ -64,7 +64,7 @@ def get_resources(parameters: dict[str, str]) -> types.ui.CallbackResultType:
|
||||
'''
|
||||
This helper is designed as a callback for Project Selector
|
||||
'''
|
||||
api, name_from_subnets = getApi(parameters)
|
||||
api, name_from_subnets = get_api(parameters)
|
||||
|
||||
zones = [gui.choice_item(z.id, z.name) for z in api.list_availability_zones()]
|
||||
networks = [
|
||||
@ -87,7 +87,7 @@ def get_volumes(parameters: dict[str, str]) -> types.ui.CallbackResultType:
|
||||
'''
|
||||
This helper is designed as a callback for Zone Selector
|
||||
'''
|
||||
api, _ = getApi(parameters)
|
||||
api, _ = get_api(parameters)
|
||||
# Source volumes are all available for us
|
||||
# volumes = [gui.choice_item(v['id'], v['name']) for v in api.listVolumes() if v['name'] != '' and v['availability_zone'] == parameters['availabilityZone']]
|
||||
volumes = [gui.choice_item(v.id, v.name) for v in api.list_volumes() if v.name]
|
||||
@ -97,3 +97,19 @@ def get_volumes(parameters: dict[str, str]) -> types.ui.CallbackResultType:
|
||||
]
|
||||
logger.debug('Return data: %s', data)
|
||||
return data
|
||||
|
||||
def get_machines(parameters: dict[str, str]) -> types.ui.CallbackResultType:
|
||||
# Needs prov_uuid, project and region in order to work
|
||||
api = get_api(parameters)[0]
|
||||
|
||||
try:
|
||||
servers = [gui.choice_item(s.id, s.name) for s in api.list_servers() if not s.name.lower().startswith('uds')]
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
return [
|
||||
{
|
||||
'name': 'machines',
|
||||
'choices': servers,
|
||||
}
|
||||
]
|
||||
|
@ -35,14 +35,14 @@ import typing
|
||||
|
||||
from django.utils.translation import gettext_noop as _
|
||||
|
||||
from uds.core import types, consts
|
||||
from uds.core import types
|
||||
from uds.core.services import ServiceProvider
|
||||
from uds.core.ui import gui
|
||||
from uds.core.util import validators, fields
|
||||
from uds.core.util.decorators import cached
|
||||
|
||||
from .openstack import openstack_client, sanitized_name, types as openstack_types
|
||||
from .service import OpenStackLiveService
|
||||
from .service_fixed import OpenStackServiceFixed
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
@ -76,7 +76,7 @@ class OpenStackProvider(ServiceProvider):
|
||||
"""
|
||||
|
||||
# : What kind of services we offer, this are classes inherited from Service
|
||||
offers = [OpenStackLiveService]
|
||||
offers = [OpenStackLiveService, OpenStackServiceFixed]
|
||||
# : 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 gettext_noop)
|
||||
|
@ -45,6 +45,7 @@ from uds.core.util.decorators import cached
|
||||
|
||||
from .openstack import openstack_client, sanitized_name, types as openstack_types
|
||||
from .service import OpenStackLiveService
|
||||
from .service_fixed import OpenStackServiceFixed
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
@ -79,7 +80,7 @@ class OpenStackProviderLegacy(ServiceProvider):
|
||||
"""
|
||||
|
||||
# : What kind of services we offer, this are classes inherited from Service
|
||||
offers = [OpenStackLiveService]
|
||||
offers = [OpenStackLiveService, OpenStackServiceFixed]
|
||||
# : 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 gettext_noop)
|
||||
|
@ -90,7 +90,7 @@ class OpenStackLivePublication(Publication, autoserializable.AutoSerializable):
|
||||
Realizes the publication of the service
|
||||
"""
|
||||
self._name = self.service().sanitized_name(
|
||||
'UDSP ' + self.servicepool_name() + "-" + str(self.revision())
|
||||
'UDS-P-' + self.servicepool_name() + "-" + str(self.revision())
|
||||
)
|
||||
self._reason = '' # No error, no reason for it
|
||||
self._destroy_after = False
|
||||
|
@ -222,9 +222,6 @@ class OpenStackLiveService(services.Service):
|
||||
tenants = [gui.choice_item(t.id, t.name) for t in api.list_projects()]
|
||||
self.project.set_choices(tenants)
|
||||
|
||||
# So we can instantiate parent to get API
|
||||
logger.debug(self.provider().serialize())
|
||||
|
||||
self.prov_uuid.value = self.provider().get_uuid()
|
||||
|
||||
@property
|
||||
|
240
server/src/uds/services/OpenStack/service_fixed.py
Normal file
240
server/src/uds/services/OpenStack/service_fixed.py
Normal file
@ -0,0 +1,240 @@
|
||||
#
|
||||
# Copyright (c) 2012-2022 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import collections.abc
|
||||
import logging
|
||||
import typing
|
||||
|
||||
from django.utils.translation import gettext
|
||||
from django.utils.translation import gettext_noop as _
|
||||
|
||||
from uds.core import exceptions, services, types
|
||||
from uds.core.services.specializations.fixed_machine.fixed_service import FixedService
|
||||
from uds.core.services.specializations.fixed_machine.fixed_userservice import FixedUserService
|
||||
from uds.core.ui import gui
|
||||
from uds.core.util import log
|
||||
|
||||
from . import helpers
|
||||
from .deployment_fixed import OpenStackUserServiceFixed
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
from uds import models
|
||||
from .openstack import openstack_client, types as openstack_types
|
||||
|
||||
from .provider import OpenStackProvider
|
||||
from .provider_legacy import OpenStackProviderLegacy
|
||||
|
||||
AnyOpenStackProvider: typing.TypeAlias = typing.Union[OpenStackProvider, OpenStackProviderLegacy]
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OpenStackServiceFixed(FixedService): # pylint: disable=too-many-public-methods
|
||||
"""
|
||||
OpenStack fixed machines service.
|
||||
"""
|
||||
|
||||
type_name = _('OpenStack Fixed Machines')
|
||||
type_type = 'OpenStackFixedService'
|
||||
type_description = _('OpenStack Services based on fixed machines.')
|
||||
icon_file = 'service.png'
|
||||
|
||||
can_reset = True
|
||||
|
||||
# : Types of publications (preparated data for deploys)
|
||||
# : In our case, we do no need a publication, so this is None
|
||||
publication_type = None
|
||||
# : Types of deploys (services in cache and/or assigned to users)
|
||||
user_service_type = OpenStackUserServiceFixed
|
||||
|
||||
allowed_protocols = types.transports.Protocol.generic_vdi()
|
||||
services_type_provided = types.services.ServiceType.VDI
|
||||
|
||||
# Gui
|
||||
token = FixedService.token
|
||||
|
||||
# Now the form part
|
||||
region = gui.ChoiceField(
|
||||
label=_('Region'),
|
||||
order=1,
|
||||
tooltip=_('Service region'),
|
||||
required=True,
|
||||
readonly=True,
|
||||
)
|
||||
project = gui.ChoiceField(
|
||||
label=_('Project'),
|
||||
order=2,
|
||||
fills={
|
||||
'callback_name': 'osGetMachines',
|
||||
'function': helpers.get_machines,
|
||||
'parameters': ['prov_uuid', 'project', 'region'],
|
||||
},
|
||||
tooltip=_('Project for this service'),
|
||||
required=True,
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
machines = FixedService.machines
|
||||
|
||||
prov_uuid = gui.HiddenField()
|
||||
|
||||
_api: typing.Optional['openstack_client.OpenstackClient'] = None
|
||||
|
||||
@property
|
||||
def api(self) -> 'openstack_client.OpenstackClient':
|
||||
if not self._api:
|
||||
self._api = self.provider().api(projectid=self.project.value, region=self.region.value)
|
||||
|
||||
return self._api
|
||||
|
||||
|
||||
def initialize(self, values: 'types.core.ValuesType') -> None:
|
||||
"""
|
||||
Loads the assigned machines from storage
|
||||
"""
|
||||
if values:
|
||||
if not self.machines.value:
|
||||
raise exceptions.ui.ValidationError(gettext('We need at least a machine'))
|
||||
|
||||
with self.storage.as_dict() as d:
|
||||
d['userservices_limit'] = len(self.machines.as_list())
|
||||
|
||||
# Remove machines not in values from "assigned" set
|
||||
self._save_assigned_machines(self._get_assigned_machines() & set(self.machines.as_list()))
|
||||
self.token.value = self.token.value.strip()
|
||||
with self.storage.as_dict() as d:
|
||||
self.userservices_limit = d.get('userservices_limit', 0)
|
||||
|
||||
def init_gui(self) -> None:
|
||||
api = self.provider().api()
|
||||
|
||||
# Checks if legacy or current openstack provider
|
||||
parent = typing.cast('OpenStackProvider', self.provider()) if not self.provider().legacy else None
|
||||
|
||||
if parent and parent.region.value:
|
||||
regions = [gui.choice_item(parent.region.value, parent.region.value)]
|
||||
else:
|
||||
regions = [gui.choice_item(r.id, r.name) for r in api.list_regions()]
|
||||
|
||||
self.region.set_choices(regions)
|
||||
|
||||
if parent and parent.tenant.value:
|
||||
tenants = [gui.choice_item(parent.tenant.value, parent.tenant.value)]
|
||||
else:
|
||||
tenants = [gui.choice_item(t.id, t.name) for t in api.list_projects()]
|
||||
self.project.set_choices(tenants)
|
||||
|
||||
self.prov_uuid.value = self.provider().get_uuid()
|
||||
|
||||
def provider(self) -> 'AnyOpenStackProvider':
|
||||
return typing.cast('AnyOpenStackProvider', super().provider())
|
||||
|
||||
def get_machine_info(self, vmid: str) -> 'openstack_types.ServerInfo':
|
||||
return self.api.get_server(vmid)
|
||||
|
||||
def is_avaliable(self) -> bool:
|
||||
return self.provider().is_available()
|
||||
|
||||
def enumerate_assignables(self) -> collections.abc.Iterable[types.ui.ChoiceItem]:
|
||||
# Obtain machines names and ids for asignables
|
||||
servers = {server.id:server.name for server in self.api.list_servers() if not server.name.startswith('UDS-')}
|
||||
|
||||
assigned_servers = self._get_assigned_machines()
|
||||
return [
|
||||
gui.choice_item(k, servers.get(k, 'Unknown!'))
|
||||
for k in self.machines.as_list()
|
||||
if k not in assigned_servers
|
||||
]
|
||||
|
||||
def assign_from_assignables(
|
||||
self, assignable_id: str, user: 'models.User', userservice_instance: 'services.UserService'
|
||||
) -> types.states.TaskState:
|
||||
proxmox_service_instance = typing.cast(OpenStackUserServiceFixed, userservice_instance)
|
||||
assigned_vms = self._get_assigned_machines()
|
||||
if assignable_id not in assigned_vms:
|
||||
assigned_vms.add(assignable_id)
|
||||
self._save_assigned_machines(assigned_vms)
|
||||
return proxmox_service_instance.assign(assignable_id)
|
||||
|
||||
return proxmox_service_instance.error('VM not available!')
|
||||
|
||||
def process_snapshot(self, remove: bool, userservice_instance: FixedUserService) -> None:
|
||||
return # No snapshots support
|
||||
|
||||
def get_and_assign_machine(self) -> str:
|
||||
found_vmid: typing.Optional[str] = None
|
||||
try:
|
||||
assigned_vms = self._get_assigned_machines()
|
||||
for checking_vmid in self.machines.as_list():
|
||||
if checking_vmid not in assigned_vms: # Not already assigned
|
||||
try:
|
||||
# Invoke to check it exists, do not need to store the result
|
||||
if self.api.get_server(checking_vmid).status.is_lost():
|
||||
raise Exception('Machine not found') # Process on except
|
||||
found_vmid = checking_vmid
|
||||
break
|
||||
except Exception: # Notifies on log, but skipt it
|
||||
self.provider().do_log(
|
||||
log.LogLevel.WARNING, 'Machine {} not accesible'.format(found_vmid)
|
||||
)
|
||||
logger.warning(
|
||||
'The service has machines that cannot be checked on proxmox (connection error or machine has been deleted): %s',
|
||||
found_vmid,
|
||||
)
|
||||
|
||||
if found_vmid:
|
||||
assigned_vms.add(found_vmid)
|
||||
self._save_assigned_machines(assigned_vms)
|
||||
except Exception as e: #
|
||||
logger.debug('Error getting machine: %s', e)
|
||||
raise Exception('No machine available')
|
||||
|
||||
if not found_vmid:
|
||||
raise Exception('All machines from list already assigned.')
|
||||
|
||||
return str(found_vmid)
|
||||
|
||||
def get_first_network_mac(self, vmid: str) -> str:
|
||||
return self.api.get_server(vmid).addresses[0].mac
|
||||
|
||||
def get_guest_ip_address(self, vmid: str) -> str:
|
||||
return self.api.get_server(vmid).addresses[0].ip
|
||||
|
||||
def get_machine_name(self, vmid: str) -> str:
|
||||
return self.api.get_server(vmid).name
|
||||
|
||||
def remove_and_free_machine(self, vmid: str) -> str:
|
||||
try:
|
||||
self._save_assigned_machines(self._get_assigned_machines() - {str(vmid)}) # Remove from assigned
|
||||
return types.states.State.FINISHED
|
||||
except Exception as e:
|
||||
logger.warning('Cound not save assigned machines on fixed pool: %s', e)
|
||||
raise
|
@ -156,19 +156,52 @@ class TestOpenstackLiveDeployment(UDSTransactionTestCase):
|
||||
This test will not have keep on error active, and will create correctly
|
||||
but will error on set_ready, so it will be put on error state
|
||||
"""
|
||||
pass
|
||||
|
||||
"""
|
||||
Test the user service
|
||||
"""
|
||||
for keep_on_error in (True, False):
|
||||
for prov in (fixtures.create_provider_legacy(), fixtures.create_provider()):
|
||||
with fixtures.patch_provider_api(legacy=prov.legacy) as _api:
|
||||
service = fixtures.create_live_service(prov, maintain_on_error=keep_on_error)
|
||||
userservice = fixtures.create_live_userservice(service=service)
|
||||
publication = userservice.publication()
|
||||
publication._template_id = 'snap1'
|
||||
|
||||
state = userservice.deploy_for_user(models.User())
|
||||
self.assertEqual(state, types.states.TaskState.RUNNING)
|
||||
|
||||
server = fixtures.get_id(fixtures.SERVERS_LIST, userservice._vmid)
|
||||
server.power_state = openstack_types.PowerState.RUNNING
|
||||
|
||||
for _counter in limited_iterator(lambda: state == types.states.TaskState.RUNNING, limit=128):
|
||||
state = userservice.check_state()
|
||||
|
||||
# Correctly created
|
||||
self.assertEqual(state, types.states.TaskState.FINISHED)
|
||||
|
||||
# We are going to force an error on set_ready
|
||||
server.status = openstack_types.ServerStatus.ERROR
|
||||
|
||||
state = userservice.set_ready()
|
||||
|
||||
if keep_on_error:
|
||||
self.assertEqual(state, types.states.TaskState.FINISHED)
|
||||
else:
|
||||
self.assertEqual(state, types.states.TaskState.ERROR)
|
||||
|
||||
def test_userservice_error_keep_create(self) -> None:
|
||||
"""
|
||||
This test will have keep on error active, and will create incorrectly
|
||||
so vm will be deleted and put on error state
|
||||
"""
|
||||
pass
|
||||
|
||||
def test_userservice_error_keep(self) -> None:
|
||||
"""
|
||||
This test will have keep on error active, and will create correctly
|
||||
but error will came later (on set_ready) and will not be put on error state
|
||||
nor deleted
|
||||
"""
|
||||
pass
|
||||
for prov in (fixtures.create_provider_legacy(), fixtures.create_provider()):
|
||||
with fixtures.patch_provider_api(legacy=prov.legacy) as api:
|
||||
service = fixtures.create_live_service(prov, maintain_on_eror=True)
|
||||
userservice = fixtures.create_live_userservice(service=service)
|
||||
publication = userservice.publication()
|
||||
publication._template_id = 'snap1'
|
||||
|
||||
api.create_server_from_snapshot.side_effect = Exception('Error')
|
||||
state = userservice.deploy_for_user(models.User())
|
||||
|
||||
self.assertEqual(state, types.states.TaskState.ERROR)
|
||||
|
Loading…
x
Reference in New Issue
Block a user