Skip to content

Commit

Permalink
added a couple of unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
nahumtimerman committed Jun 14, 2018
1 parent 2bb1781 commit d235c22
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
from cloudshell.cp.vcenter.models.DeployFromTemplateDetails import DeployFromTemplateDetails
from cloudshell.cp.core.models import DeployApp, DeployAppResult, SaveApp, SaveAppResult
from cloudshell.cp.vcenter.common.vcenter.folder_manager import FolderManager
from cloudshell.cp.vcenter.common.vcenter.cancellation_service import CommandCancellationService


class CommandOrchestrator(object):
Expand All @@ -65,6 +66,7 @@ def __init__(self):
"""
synchronous_task_waiter = SynchronousTaskWaiter()
cancellation_service = CommandCancellationService()
pv_service = pyVmomiService(connect=SmartConnect, disconnect=Disconnect, task_waiter=synchronous_task_waiter)
self.resource_model_parser = ResourceModelParser()
port_group_name_generator = DvPortGroupNameGenerator()
Expand Down Expand Up @@ -160,7 +162,8 @@ def __init__(self):
deployer=vm_deployer,
resource_model_parser=self.resource_model_parser,
snapshot_saver=self.snapshot_saver,
folder_manager=self.folder_manager)
folder_manager=self.folder_manager,
cancellation_service=cancellation_service)

def connect_bulk(self, context, request):
results = self.command_wrapper.execute_command_with_connection(
Expand Down
25 changes: 9 additions & 16 deletions package/cloudshell/cp/vcenter/commands/save_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@


class SaveAppCommand:
def __init__(self, pyvmomi_service, task_waiter, deployer, resource_model_parser, snapshot_saver, folder_manager):
def __init__(self, pyvmomi_service, task_waiter, deployer, resource_model_parser, snapshot_saver, folder_manager,
cancellation_service):
"""
:param pyvmomi_service:
:type pyvmomi_service: pyVmomiService
Expand All @@ -29,6 +30,7 @@ def __init__(self, pyvmomi_service, task_waiter, deployer, resource_model_parser
self.folder_manager = folder_manager
SAVE_APPS_THREAD_POOL_SIZE = int(os.getenv('SaveAppsThreadPoolSize', 10))
self._pool = ThreadPool(SAVE_APPS_THREAD_POOL_SIZE)
self.cs = cancellation_service

def save_app(self, si, logger, vcenter_data_model, reservation_id, save_app_actions, cancellation_context):
"""
Expand Down Expand Up @@ -69,10 +71,10 @@ def save_app(self, si, logger, vcenter_data_model, reservation_id, save_app_acti

error_results = [r for r in results if not r.success]
if not error_results:
self._execute_save_actions_using_pool(artifactSaversToActions,
cancellation_context,
logger,
results)
results = self._execute_save_actions_using_pool(artifactSaversToActions,
cancellation_context,
logger,
results)

return results

Expand All @@ -87,14 +89,14 @@ def _execute_save_actions_using_pool(self, artifactSaversToActions, cancellation
logger))
destroy_params.extend(self._get_destroy_params(artifactSaver, artifactSaversToActions))

if cancellation_context.is_cancelled:
if self.cs.check_if_cancelled(cancellation_context):
raise Exception('Save sandbox was cancelled')

results_before_deploy = copy.deepcopy(results)

results.extend(self._pool.map(self._save, save_params))

if cancellation_context.is_cancelled:
if self.cs.check_if_cancelled(cancellation_context):
results = results_before_deploy
for param in destroy_params:
results.append(self._destroy(param))
Expand Down Expand Up @@ -145,12 +147,3 @@ def validate_requested_save_types_supported(self, artifactSaversToActions, logge
success=False,
errorMessage=result_error_message))


def _get_async_results(async_results, thread_pool):
thread_pool.close()
thread_pool.join()
results = []
for async_result in async_results:
save_result = async_result.get()
results.append(save_result)
return results
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class CancellationException(Exception):
"""Raised when command was cancelled from the CloudShell"""

def __init__(self, message, data):
"""
:param str message:
:param dict data:
:return:
"""
# Call the base class constructor with the parameters it needs
super(CancellationException, self).__init__(message)

self.data = data if data else {}


class CommandCancellationService(object):
def check_if_cancelled(self, cancellation_context, data=None):
"""Check if command was cancelled from the CloudShell
:param cancellation_context cloudshell.shell.core.driver_context.CancellationContext instance
:param dict data: Dictionary that will be added to the cancellation exception if raised.
Use this container to add context data to the cancellation exception to be used
by the exception handler
:raises cloudshell.cp.azure.common.exceptions.cancellation_exception.CancellationException
:return:
"""
if cancellation_context and cancellation_context.is_cancelled:
raise CancellationException("Command was cancelled", data)
6 changes: 5 additions & 1 deletion package/cloudshell/cp/vcenter/models/QualiDriverModels.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,13 @@ def __init__(self, environment_name, environment_path, domain, description, owne

class CancellationContext:
def __init__(self):
self.is_cancelled = False
self._is_cancelled = False
""":type : bool"""

@property
def is_cancelled(self):
return self._is_cancelled


class AutoLoadCommandContext:
def __init__(self, connectivity, resource):
Expand Down
101 changes: 85 additions & 16 deletions package/cloudshell/tests/test_commands/test_save_app.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from unittest import TestCase
from mock import Mock
from mock import Mock, PropertyMock
from uuid import uuid4 as guid

from cloudshell.cp.vcenter.commands.save_app import SaveAppCommand
from cloudshell.cp.core.models import SaveApp, SaveAppParams

from cloudshell.cp.vcenter.common.vcenter.folder_manager import FolderManager
from cloudshell.cp.vcenter.models.DeployFromTemplateDetails import DeployFromTemplateDetails

from cloudshell.cp.vcenter.models.QualiDriverModels import CancellationContext

class MockResourceParser(object):
@staticmethod
Expand All @@ -18,19 +18,25 @@ def convert_to_resource_model(dummy, callable):
class TestSaveAppCommand(TestCase):
def setUp(self):
self.pyvmomi_service = Mock()
self.cancellation_context = Mock()
self.cancellation_context.is_cancelled = False
vm = Mock()
vm.name = 'some string'
folder_manager = FolderManager(self.pyvmomi_service)
task_waiter = Mock()
self.folder_manager = FolderManager(self.pyvmomi_service, task_waiter)
self.pyvmomi_service.get_vm_by_uuid = Mock(return_value=vm)
self.cancellation_service = Mock()
self.cancellation_service.check_if_cancelled = Mock(return_value=False)
clone_result = Mock(vmName='whatever')
self.deployer = Mock()
self.deployer.deploy_clone_from_vm = Mock(return_value=clone_result)
self.save_command = SaveAppCommand(pyvmomi_service=self.pyvmomi_service,
task_waiter=Mock(),
deployer=Mock(),
deployer=self.deployer,
resource_model_parser=MockResourceParser(),
snapshot_saver=Mock(),
folder_manager=folder_manager)
clone_result = Mock()
clone_result.vmName = 'whatever'
self.save_command.deployer.deploy_clone_from_vm = Mock(return_value=clone_result)
folder_manager=self.folder_manager,
cancellation_service=self.cancellation_service)

def test_save_runs_successfully(self):
# receive a save request with 2 actions, return a save response with 2 results.
Expand All @@ -48,7 +54,7 @@ def test_save_runs_successfully(self):
vcenter_data_model=vcenter_data_model,
reservation_id='abc',
save_app_actions=[save_action1, save_action2],
cancellation_context=None)
cancellation_context=self.cancellation_context)

# Assert
self.assertTrue(result[0].type == 'SaveApp')
Expand All @@ -59,6 +65,65 @@ def test_save_runs_successfully(self):
self.assertTrue(result[1].actionId == save_action2.actionId)
self.assertTrue(result[1].success)

def test_exception_thrown_if_command_cancelled_before_anything_runs(self):
save_action1 = self._create_arbitrary_save_app_action()
save_action2 = self._create_arbitrary_save_app_action()

vcenter_data_model = Mock()
vcenter_data_model.default_datacenter = 'QualiSB Cluster'
vcenter_data_model.vm_location = 'QualiFolder'

cancellation_context = Mock()
cancellation_context.is_cancelled = True

with self.assertRaises(Exception) as context:
self.save_command.save_app(si=Mock(),
logger=Mock(),
vcenter_data_model=vcenter_data_model,
reservation_id='abc',
save_app_actions=[save_action1, save_action2],
cancellation_context=cancellation_context)
self.assertIn('Save sandbox was cancelled', context.exception.message)

def test_destroy_on_vms_and_folders_if_command_cancelled_during_deploy(self):
save_action1 = self._create_arbitrary_save_app_action()
save_action2 = self._create_arbitrary_save_app_action()

vcenter_data_model = Mock()
vcenter_data_model.default_datacenter = 'QualiSB Cluster'
vcenter_data_model.vm_location = 'QualiFolder'

return_values = [False, True]

cancellation_service = Mock()
cancellation_service.check_if_cancelled = Mock(side_effect=return_values)

self.save_command = SaveAppCommand(pyvmomi_service=self.pyvmomi_service,
task_waiter=Mock(),
deployer=self.deployer,
resource_model_parser=MockResourceParser(),
snapshot_saver=Mock(),
folder_manager=self.folder_manager,
cancellation_service=cancellation_service)

result = self.save_command.save_app(si=Mock(),
logger=Mock(),
vcenter_data_model=vcenter_data_model,
reservation_id='abc',
save_app_actions=[save_action1, save_action2],
cancellation_context=self.cancellation_context)

# Assert
self.assertTrue(result[0].type == 'SaveApp')
self.assertTrue(result[0].actionId == save_action1.actionId)
self.assertTrue(result[0].errorMessage == 'Save app action {0} was cancelled'.format(save_action1.actionId))
self.assertTrue(not result[0].success)

self.assertTrue(result[1].type == 'SaveApp')
self.assertTrue(result[1].actionId == save_action2.actionId)
self.assertTrue(result[0].errorMessage == 'Save app action {0} was cancelled'.format(save_action2.actionId))
self.assertTrue(not result[1].success)

def test_nonempty_saved_sandbox_storage_replaces_default_storage(self):
# receive a save request with 2 actions, return a save response with 2 results.
# baseline test
Expand All @@ -76,7 +141,7 @@ def test_nonempty_saved_sandbox_storage_replaces_default_storage(self):
vcenter_data_model=vcenter_data_model,
reservation_id='abc',
save_app_actions=[save_action1],
cancellation_context=None)
cancellation_context=self.cancellation_context)

# Assert
deploy_method_mock = self.save_command.deployer.deploy_clone_from_vm
Expand All @@ -102,7 +167,7 @@ def test_empty_saved_sandbox_storage_does_not_replace_default_storage(self):
vcenter_data_model=vcenter_data_model,
reservation_id='abc',
save_app_actions=[save_action1],
cancellation_context=None)
cancellation_context=self.cancellation_context)

# Assert
deploy_method_mock = self.save_command.deployer.deploy_clone_from_vm
Expand Down Expand Up @@ -132,7 +197,7 @@ def test_behavior_during_save_configured_as_power_off(self):
vcenter_data_model=vcenter_data_model,
reservation_id='abc',
save_app_actions=[save_action],
cancellation_context=None)
cancellation_context=self.cancellation_context)

# Assert
self.assertTrue(result[0].type == 'SaveApp')
Expand Down Expand Up @@ -162,7 +227,7 @@ def test_behavior_during_save_configured_as_power_off_on_vcenter_model_empty_on_
vcenter_data_model=vcenter_data_model,
reservation_id='abc',
save_app_actions=[save_action],
cancellation_context=None)
cancellation_context=self.cancellation_context)

# Assert
self.assertTrue(result[0].type == 'SaveApp')
Expand All @@ -184,6 +249,8 @@ def test_create_saved_apps_folder_if_not_found_in_vcenter(self):
vcenter_data_model.default_datacenter = 'QualiSB Cluster'
vcenter_data_model.vm_location = 'QualiFolder'



vm_location_folder = Mock()

self.save_command.pyvmomi_service.get_folder = Mock(side_effect=[vm_location_folder, None, None,
Expand All @@ -195,7 +262,7 @@ def test_create_saved_apps_folder_if_not_found_in_vcenter(self):
vcenter_data_model=vcenter_data_model,
reservation_id='abc',
save_app_actions=[save_action],
cancellation_context=None)
cancellation_context=self.cancellation_context)

# Assert
self.assertTrue(result[0].type == 'SaveApp')
Expand All @@ -208,6 +275,8 @@ def test_create_saved_sandbox_folder_if_not_found_in_vcenter(self):
# show the sandbox folder under saved apps folder is created when doesnt exist
save_action = self._create_arbitrary_save_app_action()



vcenter_data_model = Mock()
vcenter_data_model.default_datacenter = 'QualiSB Cluster'
vcenter_data_model.vm_location = 'QualiFolder'
Expand All @@ -229,7 +298,7 @@ def cant_find_saved_apps_folder(si, path):
vcenter_data_model=vcenter_data_model,
reservation_id='abc',
save_app_actions=[save_action],
cancellation_context=None)
cancellation_context=self.cancellation_context)

# Assert
self.assertTrue(result[0].type == 'SaveApp')
Expand Down Expand Up @@ -264,7 +333,7 @@ def test_error_thrown_during_save(self):
vcenter_data_model=vcenter_data_model,
reservation_id='abc',
save_app_actions=[save_action],
cancellation_context=None)
cancellation_context=self.cancellation_context)

# Assert
self.assertTrue(result[0].type == 'SaveApp')
Expand Down

0 comments on commit d235c22

Please sign in to comment.