diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..aade0aa --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +after_success: coveralls +install: + - pip install -r ./test_requirements.txt + - pip install "cloudshell-core>=2.0.0,<2.1.0" + - pip install "cloudshell-automation-api>=7.1,<7.3" + - pip install "cloudshell-shell-core>=2.3.0,<2.4.0" + - pip install pytest-cov +language: python +notifications: + webhools: "https://qualisystemslab-cloudshell-orch-sandbox.getbadges.io/api/app/webhook/96453247-3989-49f9-a6c0-201036e14a70" +python: + - "2.7" +script: + py.test --cov=sandbox_scripts diff --git a/README.md b/README.md index e4a7736..f2b274a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ # cloudshell-orch-sandbox + +[![Coverage Status](https://coveralls.io/repos/github/QualiSystemsLab/cloudshell-orch-sandbox/badge.svg?branch=develop)](https://coveralls.io/github/QualiSystemsLab/cloudshell-orch-sandbox?branch=develop) [![Build Status](https://travis-ci.org/QualiSystemsLab/cloudshell-orch-sandbox.svg?branch=develop)](https://travis-ci.org/QualiSystemsLab/cloudshell-orch-sandbox) + Package for sandbox orchestration script functions diff --git a/sandbox_scripts/QualiEnvironmentUtils/QualiUtils.py b/sandbox_scripts/QualiEnvironmentUtils/QualiUtils.py index 43fc226..b030cdb 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/QualiUtils.py +++ b/sandbox_scripts/QualiEnvironmentUtils/QualiUtils.py @@ -7,4 +7,4 @@ def __init__(self, name, message): self.name = name def __str__(self): - return 'CloudShell error at ' + self.name + '. Error is:' + self.message + return 'CloudShell error at ' + self.name + '. Error is: ' + self.message diff --git a/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py b/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py index d4d710a..4193517 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py +++ b/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py @@ -4,9 +4,6 @@ from cloudshell.helpers.scripts import cloudshell_scripts_helpers as helpers from os.path import * -from quali_utils.quali_packaging import PackageEditor -from quali_api_client import QualiAPIClient - SEVERITY_INFO = 20 SEVERITY_ERROR = 40 @@ -46,15 +43,14 @@ def __init__(self, reservation_id, logger): # ---------------------------------- # ---------------------------------- - def write_message_to_output(self, message, severity_level=SEVERITY_INFO): + def _write_message_to_output(self, message, severity_level=SEVERITY_INFO): """ Write a message to the output window """ - if severity_level == SEVERITY_INFO: self.api_session.WriteMessageToReservationOutput(self.id, message) elif severity_level == SEVERITY_ERROR: - self.api_session.WriteMessageToReservationOutput(self.id, '' + message + '') + self.api_session.WriteMessageToReservationOutput(self.id, '' + message + '') # ---------------------------------- def report_error(self, error_message, raise_error=True, write_to_output_window=False): @@ -64,9 +60,10 @@ def report_error(self, error_message, raise_error=True, write_to_output_window=F :param bool raise_error: Do you want to throw an exception :param bool write_to_output_window: Would you like to write the message to the output window """ - self._logger.error(error_message) + if self._logger: + self._logger.error(error_message) if write_to_output_window: - self.write_message_to_output(error_message, SEVERITY_ERROR) + self._write_message_to_output(error_message, SEVERITY_ERROR) if raise_error: raise QualiError(self.id, error_message) @@ -77,9 +74,10 @@ def report_info(self, message, write_to_output_window=False): :param str message: The message you would like to present :param bool write_to_output_window: Would you like to write the message to the output window? """ - self._logger.info(message) + if self._logger: + self._logger.info(message) if write_to_output_window: - self.write_message_to_output(message, SEVERITY_INFO) + self._write_message_to_output(message, SEVERITY_INFO) # ---------------------------------- def get_root_resources(self): @@ -127,9 +125,6 @@ def get_details(self): """ try: return self.api_session.GetReservationDetails(self.id) - except QualiError as qe: - err = "Failed to get the Sandbox's details. " + str(qe) - self.report_error(error_message=err) except: err = "Failed to get the Sandbox's details. Unexpected error: " + str(sys.exc_info()[0]) self.report_error(error_message=err) @@ -170,7 +165,8 @@ def activate_connectors(self, write_to_output=True): bi_endpoints.append(endpoint.Target) bi_endpoints.append(endpoint.Source) if not bi_endpoints: - self.report_info(message="No connectors to connect for reservation {0}".format(self.id)) + self.report_info(message="No connectors to connect for reservation {0}".format(self.id), + write_to_output_window=write_to_output) return self.api_session.ConnectRoutesInReservation(self.id, bi_endpoints, 'bi') self.report_info(message="Connectors connected", write_to_output_window=write_to_output) @@ -203,7 +199,8 @@ def activate_routes(self, write_to_output=True): uni_endpoints.append(route_endpoint.Target) if not bi_endpoints and not uni_endpoints: - self.report_info(message="No routes to connect for reservation {0}".format(self.id)) + self.report_info(message="No routes to connect for reservation {0}".format(self.id), + write_to_output_window=write_to_output) return if bi_endpoints: self.api_session.ConnectRoutesInReservation(self.id, bi_endpoints, 'bi') @@ -256,31 +253,19 @@ def enqueue_command(self, commandName, commandInputs=[], printOutput=False): # ----------------------------------------- # ----------------------------------------- def save_sandbox_as_blueprint(self, blueprint_name, write_to_output=True): - snapshot_exist = True - try: - full_path = None - tp = self.api_session.GetActiveTopologyNames() - - for value in tp.Topologies: - filename = basename(value) - if filename == blueprint_name: - full_path = value - break - - if full_path is None: - snapshot_exist = False + #TODO - fullpath should be passed as a param to the function and not hard coded + # save the current Sandbox as a new Blueprint with the given snapshot name + fullpath = 'Snapshots' + self.api_session.SaveReservationAsTopology(self.id, topologyName=blueprint_name,folderFullPath=fullpath, includeInactiveRoutes=True) except CloudShellAPIError as error: err = "Failed to save sandbox as blueprint. " + error.message - self.report_error(error_message=err, write_to_output_window=write_to_output) - if snapshot_exist: - err = "Blueprint " + blueprint_name + " already exist. Please select a different name." - self.report_error(error_message=err, write_to_output_window=write_to_output) - raise Exception('Blueprint name already exist. Please select a different name.') - # save the current Sandbox as a new Blueprint with the given snapshot name - fullpath = 'Snapshots' - self.api_session.SaveReservationAsTopology(self.id, topologyName=blueprint_name,folderFullPath=fullpath, includeInactiveRoutes=True) + self.report_error(error_message=err, raise_error=True, write_to_output_window=write_to_output) + # if snapshot_exist: + # err = "Blueprint " + blueprint_name + " already exist. Please select a different name." + # self.report_error(error_message=err, write_to_output_window=write_to_output) + # raise Exception('Blueprint name already exist. Please select a different name.') #update the new snapshot with the user as owner username = helpers.get_reservation_context_details().owner_user @@ -292,8 +277,8 @@ def save_sandbox_as_blueprint(self, blueprint_name, write_to_output=True): # ----------------------------------------- def is_abstract(self, resource_alias): for abstract_resource in self.blueprint_details.AbstractResources: - if resource_alias == abstract_resource.Alias: - return True + if resource_alias == abstract_resource.Alias: + return True return False # ----------------------------------------- @@ -312,8 +297,8 @@ def get_storage_server_resource(self): def get_config_set_pool_resource(self): root_resources = self.get_root_resources() for resource in root_resources: - if resource.model.lower() == 'config set pool': - return resource + if resource.model.lower() == 'config set pool': + return resource return None diff --git a/sandbox_scripts/QualiEnvironmentUtils/SnapshotManager.py b/sandbox_scripts/QualiEnvironmentUtils/SnapshotManager.py new file mode 100644 index 0000000..6b4e6cd --- /dev/null +++ b/sandbox_scripts/QualiEnvironmentUtils/SnapshotManager.py @@ -0,0 +1,36 @@ +#TODO: implement this file/move code from NetworkSaveNResource +from sandbox_scripts.QualiEnvironmentUtils.StorageManager import StorageManager +from sandbox_scripts.QualiEnvironmentUtils.QualiUtils import QualiError +''' +class SnapshotManager(object): + def __init__(self, storage_client): + self.storage_client = storage_client + self.config_files_root = self.storage_client.get_configs_root() + # ---------------------------------- + # Is this Sandbox originates from a snapshot Blueprint? + # ---------------------------------- + def is_snapshot(self,fileName = " "): + # check if there is a directory with the Blueprint's name under the snapshots dir + if fileName != " ": + env_dir = self.config_files_root + '/Snapshots/' + fileName + else: + env_dir = self.config_files_root + '/Snapshots/' + self.sandbox.Blueprint_name + + env_dir = env_dir.replace(' ', '_') + return self.storage_client.dir_exist(env_dir) + + # ---------------------------------- + # delete file name on storage + # ---------------------------------- + def delete_src_file(self,fileName): + + env_dir = self.config_files_root + '/Snapshots/' + fileName + env_dir = env_dir.replace(' ', '_') + self.storage_client.delete(env_dir) + + # ---------------------------------- + def create_snapshot_dir(self,snapshot_name): + env_dir = self.config_files_root + '/Snapshots/' + snapshot_name.strip() + if not self.storage_client.dir_exist(env_dir): + self.storage_client.create_dir(env_dir, write_to_output=True) +''' \ No newline at end of file diff --git a/sandbox_scripts/QualiEnvironmentUtils/tests/__init__.py b/sandbox_scripts/QualiEnvironmentUtils/tests/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/sandbox_scripts/QualiEnvironmentUtils/tests/__init__.py @@ -0,0 +1 @@ + diff --git a/sandbox_scripts/QualiEnvironmentUtils/tests/test_Sandbox.py b/sandbox_scripts/QualiEnvironmentUtils/tests/test_Sandbox.py new file mode 100644 index 0000000..85b6d42 --- /dev/null +++ b/sandbox_scripts/QualiEnvironmentUtils/tests/test_Sandbox.py @@ -0,0 +1,407 @@ +import unittest +from mock import patch, Mock,call +from sandbox_scripts.QualiEnvironmentUtils.Sandbox import SandboxBase +from sandbox_scripts.QualiEnvironmentUtils.QualiUtils import QualiError +from cloudshell.api.cloudshell_api import ReservationDescriptionInfo +import json +import os +from cloudshell.api.common_cloudshell_api import CloudShellAPIError + +resContext = '''{"id":"5487c6ce-d0b3-43e9-8ee7-e27af8406905", + "ownerUser":"bob", + "ownerPass":"nIqm+BG6ZGJjby5hUittVFFJASc=", + "domain":"Global", + "environmentName":"My environment", + "description":"New demo environment", + "parameters": + { "globalInputs": [], + "resourceRequirements":[], + "resourceAdditionalInfo":[]}}''' + +conContext = '''{"serverAddress": "localhost", +"adminAuthToken": "anAdminToken"}''' + + +class SandboxTests(unittest.TestCase): + @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session') + def setUp(self, mock_api_session): + os.environ['reservationContext'] = resContext + os.environ['qualiConnectivityContext'] = conContext + tli = Mock() + tli.Topologies = ["My environment"] + mock_api_session.return_value.GetActiveTopologyNames = Mock(return_value=tli) + + abstractinfo = Mock() + abstractinfo.Alias = "alias" + topoinfo = Mock() + + topoinfo.Name = "My environment" + topoinfo.AbstractResources = [abstractinfo] + mock_api_session.return_value.GetTopologyDetails = Mock(return_value=topoinfo) + mock_logger = Mock() + self.sandbox = SandboxBase(reservation_id="5487c6ce-d0b3-43e9-8ee7-e27af8406905", logger=mock_logger) + + self.mock_api_session = mock_api_session + self.mock_logger = mock_logger + + def tearDown(self): + pass + + #================================================================ + #test report_error function + def test_report_error_without_raising_error_and_without_output(self): + try: + self.sandbox.report_error(error_message="error message", + raise_error=False, + write_to_output_window=False) + except Exception as e: + self.fail("Got an exception while not expecting to: " + e.message) + + self.mock_api_session.return_value.WriteMessageToReservationOutput.assert_not_called() + + def test_report_error_with_raising_error_and_without_output(self): + with self.assertRaises(QualiError) as e: + self.sandbox.report_error(error_message="error message", + raise_error=True, + write_to_output_window=False) + + the_exception = e.exception + self.assertEqual(str(the_exception), + "CloudShell error at 5487c6ce-d0b3-43e9-8ee7-e27af8406905. Error is: error message") + self.mock_api_session.return_value.WriteMessageToReservationOutput.assert_not_called() + + def test_report_error_without_raising_error_and_with_output(self): + self.sandbox.report_error(error_message="error message", + raise_error=False, + write_to_output_window=True) + + self.mock_api_session.return_value.WriteMessageToReservationOutput.assert_called_with( + '5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'error message') + + def test_report_error_with_just_to_log(self): + self.sandbox.report_error(error_message="error message", + raise_error=False, + write_to_output_window=False) + + self.mock_api_session.return_value.WriteMessageToReservationOutput.assert_not_called() + self.mock_logger.error.assert_called_with('error message') + + #================================================================ + #test report_info function + def test_report_info_to_log_without_output(self): + self.sandbox.report_info(message="error message",write_to_output_window=False) + self.mock_api_session.return_value.WriteMessageToReservationOutput.assert_not_called() + self.mock_logger.info.assert_called_with('error message') + + def test_report_info_to_log_with_output(self): + self.sandbox.report_info(message="error message",write_to_output_window=True) + self.mock_api_session.return_value.WriteMessageToReservationOutput.\ + assert_called_with('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'error message') + self.mock_logger.info.assert_called_with('error message') + + + #================================================================ + #test get_root_resources + def test_no_resources_in_sandbox(self): + rdi = Mock() + rdi.ReservationDescription = Mock() + rdi.ReservationDescription.Resources = [] + self.mock_api_session.return_value.GetReservationDetails = Mock(return_value=rdi) + + resources = self.sandbox.get_root_resources() + self.assertEqual(resources, [], "got resources, excepted none") + + #--------------------------- + @patch('sandbox_scripts.QualiEnvironmentUtils.Sandbox.ResourceBase') + def test_get_root_resources_with_sub_resources(self, mock_resourcebase): + rdi = Mock() + resource1 = Mock() + resource1.Name = "r1" + resource1a = Mock() + resource1a.Name = "r1/r2" + resource2 = Mock() + resource2.Name = "r2" + rdi.ReservationDescription.Resources = [resource1, resource1a, resource2] + rdi.ReservationDescription.TopologiesReservedResources = [] + self.mock_api_session.return_value.GetReservationDetails = Mock(return_value=rdi) + + resources = self.sandbox.get_root_resources() + self.assertEqual(len(resources), 2, "didn't get resources, excepted two") + + #--------------------------- + @patch('sandbox_scripts.QualiEnvironmentUtils.Sandbox.ResourceBase') + def test_get_root_resources_with_services_only(self, mock_resourcebase): + rdi = Mock() + Service1 = Mock() + Service1.Name = "s1" + Service2 = Mock() + Service2.Name = "s2" + rdi.ReservationDescription.Services = [Service1,Service2] + rdi.ReservationDescription.Resources = [] + rdi.ReservationDescription.TopologiesReservedResources = [] + self.mock_api_session.return_value.GetReservationDetails = Mock(return_value=rdi) + + resources = self.sandbox.get_root_resources() + self.assertEqual(len(resources), 0, "No resources expected") + + #--------------------------- + @patch('sandbox_scripts.QualiEnvironmentUtils.Sandbox.ResourceBase') + def test_get_root_resources_with_services_and_resource(self, mock_resourcebase): + rdi = Mock() + Service1 = Mock() + Service1.Name = "s1" + Service2 = Mock() + Service2.Name = "s2" + resource1 = Mock() + resource1.Name = "r1" + rdi.ReservationDescription.Services = [Service1,Service2] + rdi.ReservationDescription.Resources = [resource1] + rdi.ReservationDescription.TopologiesReservedResources = [] + self.mock_api_session.return_value.GetReservationDetails = Mock(return_value=rdi) + + resources = self.sandbox.get_root_resources() + self.assertEqual(len(resources), 1, "No resources expected") + + #--------------------------- + @patch('sandbox_scripts.QualiEnvironmentUtils.Sandbox.ResourceBase') + def test_get_root_resources_without_sub_resources_and_with_alias(self, mock_resourcebase): + rdi = Mock() + resource1 = Mock() + resource1.Name = "r1" + trr = Mock() + trr.Name = "r1" + trr.Alias = "my r1" + trr.TopologyName = "My environment" + rdi.ReservationDescription.Resources = [resource1] + rdi.ReservationDescription.TopologiesReservedResources = [trr] + self.mock_api_session.return_value.GetReservationDetails = Mock(return_value=rdi) + + resources = self.sandbox.get_root_resources() + mock_resourcebase.assert_called_with('r1', 'my r1') + self.assertEqual(len(resources), 1, "didn't get resources, excepted one") + + #================================================================ + #test clear_all_resources_live_status + def test_clear_all_resources_live_status_no_devices(self): + rdi = Mock() + rdi.ReservationDescription.Resources = [] + rdi.ReservationDescription.TopologiesReservedResources = [] + self.mock_api_session.return_value.GetReservationDetails = Mock(return_value=rdi) + + self.sandbox.clear_all_resources_live_status() + self.mock_api_session.return_value.SetResourceLiveStatus.assert_not_called() + + #--------------------------- + @patch('sandbox_scripts.QualiEnvironmentUtils.Sandbox.ResourceBase') + def test_clear_all_resources_live_status_two_devices(self,mock_resourcebase): + resource1 = Mock() + resource1.name = "r1" + resource2 = Mock() + resource2.name = "r2" + rr = Mock() + rr = [resource1, resource2] + self.sandbox.get_root_resources = Mock(return_value=rr) + + self.sandbox.clear_all_resources_live_status() + #self.mock_api_session.return_value.SetResourceLiveStatus.assert_called() + calls = [call('r1', ''), + call('r2', '')] + self.mock_api_session.return_value.SetResourceLiveStatus.assert_has_calls(calls) + + #================================================================ + #test activate_connectors + def test_activate_connectors_no_connectors_no_output(self): + rdi = Mock() + rdi.ReservationDescription.Connectors = [] + self.mock_api_session.return_value.GetReservationDetails = Mock(return_value=rdi) + self.sandbox.activate_connectors(False) + self.mock_api_session.return_value.ConnectRoutesInReservation.assert_not_called() + self.mock_api_session.return_value.WriteMessageToReservationOutput.assert_not_called() + + #--------------------------- + def test_activate_connectors_no_connectors_with_output(self): + rdi = Mock() + rdi.ReservationDescription.Connectors = [] + self.mock_api_session.return_value.GetReservationDetails = Mock(return_value=rdi) + self.sandbox.activate_connectors(True) + self.mock_api_session.return_value.ConnectRoutesInReservation.assert_not_called() + calls = [call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'Connecting the connectors'), + call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'No connectors to connect for reservation 5487c6ce-d0b3-43e9-8ee7-e27af8406905')] + self.mock_api_session.return_value.WriteMessageToReservationOutput.assert_has_calls(calls) + + #--------------------------- + def test_activate_connectors_with_connectors_with_output(self): + rdi = Mock() + connector1 = Mock() + connector1.State = 'Disconnected' + connector1.Target = 'x' + connector1.Source = 'y' + rdi.ReservationDescription.Connectors = [connector1] + self.mock_api_session.return_value.GetReservationDetails = Mock(return_value=rdi) + self.sandbox.activate_connectors(True) + self.mock_api_session.return_value.ConnectRoutesInReservation.assert_called() + calls = [call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'Connecting the connectors'), + call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'Connectors connected')] + self.mock_api_session.return_value.WriteMessageToReservationOutput.assert_has_calls(calls) + + #--------------------------- + def test_activate_connectors_with_connectors_already_connected_with_output(self): + rdi = Mock() + connector1 = Mock() + connector1.State = 'Connected' + connector1.Target = 'x' + connector1.Source = 'y' + rdi.ReservationDescription.Connectors = [connector1] + self.mock_api_session.return_value.GetReservationDetails = Mock(return_value=rdi) + self.sandbox.activate_connectors(True) + self.mock_api_session.return_value.ConnectRoutesInReservation.assert_not_called() + calls = [call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'Connecting the connectors'), + call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'No connectors to connect for reservation 5487c6ce-d0b3-43e9-8ee7-e27af8406905')] + self.mock_api_session.return_value.WriteMessageToReservationOutput.assert_has_calls(calls) + + #--------------------------- + def test_activate_connectors_with_some_of_connectors_already_connected_with_output(self): + rdi = Mock() + connector1 = Mock() + connector1.State = 'Connected' + connector1.Target = 'x' + connector1.Source = 'y' + connector2 = Mock() + connector2.State = 'ConnectionFailed' + connector2.Target = 'a' + connector2.Source = 'z' + rdi.ReservationDescription.Connectors = [connector1,connector2] + self.mock_api_session.return_value.GetReservationDetails = Mock(return_value=rdi) + self.sandbox.activate_connectors(True) + self.mock_api_session.return_value.ConnectRoutesInReservation.assert_called_with('5487c6ce-d0b3-43e9-8ee7-e27af8406905', ['a', 'z'], 'bi') + calls = [call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'Connecting the connectors'), + call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'Connectors connected')] + self.mock_api_session.return_value.WriteMessageToReservationOutput.assert_has_calls(calls) + + #================================================================ + #test activate_routes + def test_activate_routes_no_routes_no_output(self): + rdi = Mock() + rdi.ReservationDescription.RequestedRoutesInfo = [] + self.mock_api_session.return_value.GetReservationDetails = Mock(return_value=rdi) + self.sandbox.activate_routes(False) + self.mock_api_session.return_value.ConnectRoutesInReservation.assert_not_called() + self.mock_api_session.return_value.WriteMessageToReservationOutput.assert_not_called() + + #--------------------------- + def test_activate_routes_no_routes_with_output(self): + rdi = Mock() + rdi.ReservationDescription.RequestedRoutesInfo = [] + self.mock_api_session.return_value.GetReservationDetails = Mock(return_value=rdi) + self.sandbox.activate_routes(True) + self.mock_api_session.return_value.ConnectRoutesInReservation.assert_not_called() + calls = [call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'Connecting routes'), + call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'No routes to connect for reservation 5487c6ce-d0b3-43e9-8ee7-e27af8406905')] + self.mock_api_session.return_value.WriteMessageToReservationOutput.assert_has_calls(calls) + + #--------------------------- + def test_activate_routes_with_bi_routes_with_output(self): + rdi = Mock() + route1 = Mock() + route1.RouteType = 'bi' + route1.Target = 'x' + route1.Source = 'y' + rdi.ReservationDescription.RequestedRoutesInfo = [route1] + self.mock_api_session.return_value.GetReservationDetails = Mock(return_value=rdi) + self.sandbox.activate_routes(True) + self.mock_api_session.return_value.ConnectRoutesInReservation.assert_called_with('5487c6ce-d0b3-43e9-8ee7-e27af8406905', ['x', 'y'], 'bi') + calls = [call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'Connecting routes'), + call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'Routes connected')] + self.mock_api_session.return_value.WriteMessageToReservationOutput.assert_has_calls(calls) + + #--------------------------- + def test_activate_routes_with_uni_routes_with_output(self): + rdi = Mock() + route2 = Mock() + route2.RouteType = 'uni' + route2.Target = 'x' + route2.Source = 'y' + rdi.ReservationDescription.RequestedRoutesInfo = [route2] + self.mock_api_session.return_value.GetReservationDetails = Mock(return_value=rdi) + self.sandbox.activate_routes(True) + self.mock_api_session.return_value.ConnectRoutesInReservation.assert_called_with('5487c6ce-d0b3-43e9-8ee7-e27af8406905', ['y', 'x'], 'uni') + calls = [call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'Connecting routes'), + call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'Routes connected')] + self.mock_api_session.return_value.WriteMessageToReservationOutput.assert_has_calls(calls) + + #================================================================ + #test save as blueprint + def test_save_sandbox_as_blueprint(self): + self.sandbox.save_sandbox_as_blueprint('aaa') + self.mock_api_session.return_value.SaveReservationAsTopology.assert_called_with('5487c6ce-d0b3-43e9-8ee7-e27af8406905', + folderFullPath='Snapshots', + includeInactiveRoutes=True, + topologyName='aaa') + self.mock_api_session.return_value.UpdateTopologyOwner.assert_called_with(ownerName='bob', topologyName='Snapshots/aaa') + + # --------------------------- + def test_save_sandbox_as_blueprint_invalid_topo_name(self): + self.mock_api_session.return_value.SaveReservationAsTopology.side_effect = CloudShellAPIError(100,"Invalid name","") + with self.assertRaises(QualiError) as e: + self.sandbox.save_sandbox_as_blueprint('a@aa') + the_exception = e.exception + self.assertEqual(str(the_exception), + "CloudShell error at 5487c6ce-d0b3-43e9-8ee7-e27af8406905. Error is: Failed to save sandbox as blueprint. Invalid name") + self.mock_api_session.return_value.SaveReservationAsTopology.assert_called_with('5487c6ce-d0b3-43e9-8ee7-e27af8406905', + folderFullPath='Snapshots', + includeInactiveRoutes=True, + topologyName='a@aa') + self.mock_api_session.return_value.UpdateTopologyOwner.assert_not_called() + + #================================================================ + #test power_on_vms + def test_power_on_vms_vm_and_resource(self): + + resource1 = Mock() + resource1.name = "r1" + resource1.is_app.return_value = True + resource2 = Mock() + resource2.name = "r2" + resource2.is_app.return_value = False + rr = Mock() + rr = [resource1, resource2] + self.sandbox.get_root_resources = Mock(return_value=rr) + self.sandbox.power_on_vms() + self.mock_api_session.return_value.ExecuteResourceConnectedCommand.assert_called_with('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'r1', 'PowerOn', 'power') + + # --------------------------- + def test_power_on_vms_only_vms(self): + + resource1 = Mock() + resource1.name = "r1" + resource1.is_app.return_value = True + resource2 = Mock() + resource2.name = "r2" + resource2.is_app.return_value = True + rr = Mock() + rr = [resource1, resource2] + self.sandbox.get_root_resources = Mock(return_value=rr) + self.sandbox.power_on_vms() + calls = [call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'r1', 'PowerOn', 'power'), + call('5487c6ce-d0b3-43e9-8ee7-e27af8406905', 'r2', 'PowerOn', 'power')] + + self.mock_api_session.return_value.ExecuteResourceConnectedCommand.assert_has_calls(calls) + + # --------------------------- + def test_power_on_vms_no_vms(self): + + resource1 = Mock() + resource1.name = "r1" + resource1.is_app.return_value = False + resource2 = Mock() + resource2.name = "r2" + resource2.is_app.return_value = False + rr = Mock() + rr = [resource1, resource2] + self.sandbox.get_root_resources = Mock(return_value=rr) + self.sandbox.power_on_vms() + self.mock_api_session.return_value.ExecuteResourceConnectedCommand.assert_not_called() + +#get_Apps_resources +if __name__ == '__main__': + unittest.main() diff --git a/sandbox_scripts/environment/savesnapshot/SaveSnapshot.py b/sandbox_scripts/environment/savesnapshot/SaveSnapshot.py index 4f319f2..3aa1565 100644 --- a/sandbox_scripts/environment/savesnapshot/SaveSnapshot.py +++ b/sandbox_scripts/environment/savesnapshot/SaveSnapshot.py @@ -1,8 +1,7 @@ # coding=utf-8 -from sandbox_scripts.QualiEnvironmentUtils.Networking.NetworkingSaveNRestore import * from cloudshell.helpers.scripts import cloudshell_scripts_helpers as helpers -from sandbox_scripts.profiler.env_profiler import profileit +from sandbox_scripts.helpers.Networking.NetworkingSaveNRestore import * class EnvironmentSaveSnapshot: diff --git a/sandbox_scripts/environment/setup/setup_VM.py b/sandbox_scripts/environment/setup/setup_VM.py index c6f654c..eb06528 100644 --- a/sandbox_scripts/environment/setup/setup_VM.py +++ b/sandbox_scripts/environment/setup/setup_VM.py @@ -1,9 +1,5 @@ -from multiprocessing.pool import ThreadPool -from threading import Lock -from sandbox_scripts.profiler.env_profiler import profileit -from sandbox_scripts.QualiEnvironmentUtils.Networking.NetworkingSaveNRestore import * -from cloudshell.core.logger import qs_logger -from sandbox_scripts.helpers.resource_helpers import * +from sandbox_scripts.helpers.Networking.NetworkingSaveNRestore import * + class EnvironmentSetupVM(object): @@ -24,6 +20,7 @@ def execute(self): reservation_details = api.GetReservationDetails(self.reservation_id) sandbox = SandboxBase(self.reservation_id, self.logger) + #TODO: don't use networking save and restore to figure if it's a snapshot setup saveNRestoreTool = NetworkingSaveRestore(sandbox) if saveNRestoreTool.get_storage_client(): if saveNRestoreTool.is_snapshot(): diff --git a/sandbox_scripts/environment/setup/setup_resources.py b/sandbox_scripts/environment/setup/setup_resources.py index b81e4d5..fd582c4 100644 --- a/sandbox_scripts/environment/setup/setup_resources.py +++ b/sandbox_scripts/environment/setup/setup_resources.py @@ -1,9 +1,11 @@ # coding=utf-8 -from sandbox_scripts.QualiEnvironmentUtils.Sandbox import * -from sandbox_scripts.QualiEnvironmentUtils.Networking.NetworkingSaveNRestore import * -from sandbox_scripts.QualiEnvironmentUtils.Networking.NetworkingHealthCheck import * -from sandbox_scripts.profiler.env_profiler import profileit - +#from sandbox_scripts.helpers.Networking.NetworkingHealthCheck import * +from cloudshell.helpers.scripts import cloudshell_scripts_helpers as helpers +from sandbox_scripts.helpers.Networking.NetworkingSaveNRestore import NetworkingSaveRestore +from sandbox_scripts.QualiEnvironmentUtils.Sandbox import SandboxBase +from cloudshell.core.logger.qs_logger import get_qs_logger +from sandbox_scripts.QualiEnvironmentUtils.QualiUtils import QualiError +import os, sys class EnvironmentSetupResources(object): def __init__(self): @@ -12,7 +14,6 @@ def __init__(self): log_group=self.reservation_id, log_category='Setup') - #@profileit(scriptName='Setup') def execute(self): sandbox = SandboxBase(self.reservation_id, self.logger) saveNRestoreTool = NetworkingSaveRestore(sandbox) @@ -35,11 +36,9 @@ def execute(self): ignore_models=['Generic TFTP server', 'Config Set Pool','Generic FTP server','netscout switch 3912'] if saveNRestoreTool.get_storage_client(): - - if saveNRestoreTool.is_snapshot(): saveNRestoreTool.load_config(config_stage='Snapshots', config_type='Running', - ignore_models=ignore_models) + ignore_models=ignore_models) else: saveNRestoreTool.load_config(config_stage='Gold', config_type='Running', diff --git a/sandbox_scripts/QualiEnvironmentUtils/Networking/__init__.py b/sandbox_scripts/environment/setup/tests/__init__.py similarity index 100% rename from sandbox_scripts/QualiEnvironmentUtils/Networking/__init__.py rename to sandbox_scripts/environment/setup/tests/__init__.py diff --git a/sandbox_scripts/environment/setup/tests/test_setup_resources.py b/sandbox_scripts/environment/setup/tests/test_setup_resources.py new file mode 100644 index 0000000..f75d181 --- /dev/null +++ b/sandbox_scripts/environment/setup/tests/test_setup_resources.py @@ -0,0 +1,68 @@ +import unittest +from mock import patch, Mock +from sandbox_scripts.environment.setup.setup_resources import EnvironmentSetupResources +import json +import os + + +resContext = '''{"id":"5487c6ce-d0b3-43e9-8ee7-e27af8406905", + "ownerUser":"bob", + "ownerPass":"nIqm+BG6ZGJjby5hUittVFFJASc=", + "domain":"Global", + "environmentName":"My environment", + "description":"New demo environment", + "parameters": + { "globalInputs": [], + "resourceRequirements":[], + "resourceAdditionalInfo":[]}}''' + +conContext = '''{"serverAddress": "localhost", +"adminAuthToken": "anAdminToken"}''' + +class SetupResourcesTests(unittest.TestCase): + + @patch('sandbox_scripts.environment.setup.setup_resources.get_qs_logger') + def setUp(self, mock_logger): + os.environ['reservationContext'] = resContext + os.environ['qualiConnectivityContext'] = conContext + self.setup_script = EnvironmentSetupResources() + + def tearDown(self): + pass + + @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session') + @patch('sandbox_scripts.environment.setup.setup_resources.SandboxBase') + @patch('sandbox_scripts.environment.setup.setup_resources.NetworkingSaveRestore') + def test_flow_ok_with_snapshots(self, mock_save, mock_sandboxbase, mock_api_session): + + #tli = Mock() + #tli.Topologies = ["My environment"] + #mock_api_session.return_value.GetActiveTopologyNames = Mock(return_value = tli) + mock_save.return_value.is_snapshot.return_value = True + self.setup_script.execute() + mock_api_session.return_value.WriteMessageToReservationOutput.assert_called_with(message='Beginning resources config load', reservationId=u'5487c6ce-d0b3-43e9-8ee7-e27af8406905') + mock_sandboxbase.return_value.clear_all_resources_live_status.assert_called_with() + mock_save.return_value.load_config.assert_called_with(config_stage='Snapshots', config_type='Running', ignore_models=['Generic TFTP server', 'Config Set Pool', 'Generic FTP server', 'netscout switch 3912']) + mock_sandboxbase.return_value.power_on_vms.assert_called_with() + mock_sandboxbase.return_value.activate_all_routes_and_connectors.assert_called_with() + + @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session') + @patch('sandbox_scripts.environment.setup.setup_resources.SandboxBase') + @patch('sandbox_scripts.environment.setup.setup_resources.NetworkingSaveRestore') + def test_flow_ok_with_gold(self, mock_save, mock_sandboxbase, mock_api_session): + + #tli = Mock() + #tli.Topologies = ["My environment"] + #mock_api_session.return_value.GetActiveTopologyNames = Mock(return_value = tli) + mock_save.return_value.is_snapshot.return_value = False + self.setup_script.execute() + mock_api_session.return_value.WriteMessageToReservationOutput.assert_called_with(message='Beginning resources config load', reservationId=u'5487c6ce-d0b3-43e9-8ee7-e27af8406905') + mock_sandboxbase.return_value.clear_all_resources_live_status.assert_called_with() + mock_save.return_value.load_config.assert_called_with(config_set_name='', config_stage='Gold', config_type='Running', ignore_models=['Generic TFTP server', 'Config Set Pool', 'Generic FTP server', 'netscout switch 3912']) + mock_sandboxbase.return_value.power_on_vms.assert_called_with() + mock_sandboxbase.return_value.activate_all_routes_and_connectors.assert_called_with() + + + +if __name__ == '__main__': + unittest.main() diff --git a/sandbox_scripts/environment/teardown/teardown_VM.py b/sandbox_scripts/environment/teardown/teardown_VM.py index 40a8f38..29b8c4c 100644 --- a/sandbox_scripts/environment/teardown/teardown_VM.py +++ b/sandbox_scripts/environment/teardown/teardown_VM.py @@ -1,11 +1,10 @@ -from sandbox_scripts.QualiEnvironmentUtils.Networking.NetworkingSaveNRestore import * +from multiprocessing.pool import ThreadPool +from threading import Lock + from cloudshell.core.logger import qs_logger -from sandbox_scripts.profiler.env_profiler import profileit -from sandbox_scripts.helpers.resource_helpers import get_vm_custom_param, get_resources_created_in_res +from sandbox_scripts.helpers.Networking.NetworkingSaveNRestore import * -from multiprocessing.pool import ThreadPool -from threading import Lock class EnvironmentTeardownVM: REMOVE_DEPLOYED_RESOURCE_ERROR = 153 diff --git a/sandbox_scripts/environment/teardown/teardown_resources.py b/sandbox_scripts/environment/teardown/teardown_resources.py index 0c28f36..781cb0d 100644 --- a/sandbox_scripts/environment/teardown/teardown_resources.py +++ b/sandbox_scripts/environment/teardown/teardown_resources.py @@ -1,7 +1,7 @@ # coding=utf-8 -from sandbox_scripts.QualiEnvironmentUtils.Networking.NetworkingSaveNRestore import * from cloudshell.core.logger import qs_logger -from sandbox_scripts.profiler.env_profiler import profileit + +from sandbox_scripts.helpers.Networking.NetworkingSaveNRestore import * class EnvironmentTeardownResources: diff --git a/sandbox_scripts/QualiEnvironmentUtils/Networking/NetworkingHealthCheck.py b/sandbox_scripts/helpers/Networking/NetworkingHealthCheck.py similarity index 100% rename from sandbox_scripts/QualiEnvironmentUtils/Networking/NetworkingHealthCheck.py rename to sandbox_scripts/helpers/Networking/NetworkingHealthCheck.py diff --git a/sandbox_scripts/QualiEnvironmentUtils/Networking/NetworkingSaveNRestore.py b/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py similarity index 99% rename from sandbox_scripts/QualiEnvironmentUtils/Networking/NetworkingSaveNRestore.py rename to sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py index 580d5c2..f144ccf 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/Networking/NetworkingSaveNRestore.py +++ b/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py @@ -11,17 +11,6 @@ from sandbox_scripts.QualiEnvironmentUtils.StorageManager import StorageManager -class image_struct: - def __init__(self, path, version): - self.path = path - self.version = version - -class load_result_struct: - def __init__(self, resource_name): - self.run_result = True - self.resource_name = resource_name - self.message = "" - class NetworkingSaveRestore(object): def __init__(self, sandbox): """ @@ -447,4 +436,16 @@ def is_resources_in_reservation_to_restore(self,ignore_models): # ---------------------------------- # ---------------------------------- def get_storage_client(self): - return self.storage_client \ No newline at end of file + return self.storage_client + +class image_struct: + def __init__(self, path, version): + self.path = path + self.version = version + +class load_result_struct: + def __init__(self, resource_name): + self.run_result = True + self.resource_name = resource_name + self.message = "" + diff --git a/sandbox_scripts/helpers/Networking/__init__.py b/sandbox_scripts/helpers/Networking/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test_requirements.txt b/test_requirements.txt new file mode 100644 index 0000000..3428dcd --- /dev/null +++ b/test_requirements.txt @@ -0,0 +1,8 @@ +nose +coverage +unittest2 +mock +jsonpickle +nose-exclude +coveralls +