From 8aec47042e33cd8de03f4af25c1e8d1cc9415053 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Mon, 26 Jun 2017 10:40:08 -0700 Subject: [PATCH 01/50] minor change to pass where in teardown for support of power control --- sandbox_scripts/helpers/Networking/save_restore_mgr.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sandbox_scripts/helpers/Networking/save_restore_mgr.py b/sandbox_scripts/helpers/Networking/save_restore_mgr.py index dc219b5..ac4c160 100644 --- a/sandbox_scripts/helpers/Networking/save_restore_mgr.py +++ b/sandbox_scripts/helpers/Networking/save_restore_mgr.py @@ -20,7 +20,8 @@ def load_config(self, config_stage, config_type, restore_method="Override", conf ignore_models=ignore_models, write_to_output=write_to_output, remove_temp_files=remove_temp_files, - use_Config_file_path_attr=use_Config_file_path_attr) + use_Config_file_path_attr=use_Config_file_path_attr, + in_teardown_mode=in_teardown_mode) self.vm_save_restore.load_config(config_stage=config_stage, config_set_name=config_set_name, ignore_models=ignore_models,write_to_output=write_to_output, From 4a75aab5b5fc178d04072f9e5fb372fed9177885 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Mon, 26 Jun 2017 10:40:36 -0700 Subject: [PATCH 02/50] changes for adding pwer control --- .../Networking/NetworkingSaveNRestore.py | 98 ++++++++++++++++--- 1 file changed, 87 insertions(+), 11 deletions(-) diff --git a/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py b/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py index 7bd0299..4a5dbf1 100644 --- a/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py +++ b/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py @@ -1,6 +1,6 @@ # coding=utf-8 import csv -import os +import os, datetime import tempfile from multiprocessing.pool import ThreadPool from threading import Lock @@ -44,7 +44,8 @@ def __init__(self, sandbox): # e.g. tftp://configs/Base/svl290-gg07-sw1_c3850.cfg # ---------------------------------- def load_config(self, config_stage, config_type, restore_method="Override", config_set_name='', ignore_models=None, - write_to_output=True, remove_temp_files=False, use_Config_file_path_attr=False): + write_to_output=True, remove_temp_files=False, use_Config_file_path_attr=False, + in_teardown_mode=False): """ Load the configuration from config files on the Blueprint's devices :param str config_stage: The stage of the config e.g Gold, Base @@ -81,7 +82,7 @@ def load_config(self, config_stage, config_type, restore_method="Override", conf async_results = [pool.apply_async(self._run_asynch_load, (resource, images_path_dict, root_path, ignore_models, config_stage, health_check_attempts, lock, - use_Config_file_path_attr)) + use_Config_file_path_attr,in_teardown_mode)) for resource in root_resources] pool.close() @@ -120,18 +121,35 @@ def _remove_temp_config_files(self): # ---------------------------------- # ---------------------------------- def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, config_stage, - health_check_attempts, lock, - use_Config_file_path_attr): + health_check_attempts, lock, use_Config_file_path_attr, in_teardown_mode): message = "" # run_status = True saved_artifact_info = None additionalinfo = '' load_result = rsc_run_result_struct(resource.name) # Check if needs to load the config to the device + is_power_ctrl_ok = self._is_power_ctrl_ok(resource, ignore_models=ignore_models) + if is_power_ctrl_ok: + # Attempt PowerOn + try: + pwr_cmd = resource.has_connected_power_on() + if pwr_cmd > "": + resource.execute_connected_command(self.sandbox.id, pwr_cmd, tag='power') + self.sandbox.report_info(resource.name + ' - powered ON',write_to_output_window=True) + else: + pwr_cmd = resource.has_power_on() + if pwr_cmd > "": + resource.execute_command(self.sandbox.id, pwr_cmd) + self.sandbox.report_info(resource.name + ' - powered on',write_to_output_window=True) + else: + self.sandbox.report_info(resource.name + ' - no power on command found', + write_to_output_window=True) + except Exception as ex: + print resource.name + " power on fault. " + ex.message + load_config_to_device = self._is_load_config_to_device(resource, ignore_models=ignore_models) if load_config_to_device: - - self.sandbox.report_info(resource.name + " starting health check", write_to_output_window=True) + self.sandbox.report_info(resource.name + " - starting health check", write_to_output_window=True) health_check_result = resource.health_check(self.sandbox.id, health_check_attempts) if health_check_result == "": self.sandbox.report_info(resource.name + " -- Initial Health Check Passed.") @@ -140,6 +158,7 @@ def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, with lock: config_path = self._get_concrete_config_file_path(root_path, resource, config_stage, write_to_output=False) + self.sandbox.report_info("Config path for " + resource.alias + ": " + str(config_path)) if use_Config_file_path_attr: resource.set_attribute_value('Config file path', config_path) # TODO - Snapshots currently only restore configuration. We need to restore firmware as well @@ -172,13 +191,15 @@ def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, image_key = resource.model.replace(' ', '_') if image_key: + #self.sandbox.report_info("Using image key " + str(image_key) + " for " + resource.name) dict_img_version = images_path_dict[image_key].version else: # Getting here means no firmware specified in FirmwareData.csv message += "\n" + resource.name + ": NO firmware version specified in Base FirmwareData.csv" # same image version - Only load config (running override) - message += resource.name + ": loading configuration from " + config_path + #message += resource.name + ": loading configuration from " + config_path + self.sandbox.report_info(resource.name + ": loading configuration from " + config_path) if dict_img_version.lower() == version.lower(): resource.load_network_config(self.sandbox.id, config_path=config_path, config_type='Running', @@ -212,8 +233,8 @@ def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, except QualiError as qe: load_result.run_result = False - err = "\nFailed to load configuration " + additionalinfo + " for device " + resource.name + ". " + str( - qe) + err = "\nFailed to load configuration " + additionalinfo + " for device " + resource.name + \ + ". " + str(qe) message += err except Exception as ex: load_result.run_result = False @@ -230,6 +251,47 @@ def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, message += err load_result.message = message + + if is_power_ctrl_ok and in_teardown_mode: + # Attempt PowerOFF. + # If threshold is a Negative value, no power off. + # If threshold is 0-4, power off now + # If threshold is >5, check for usesage in that period with a 2minute shift ahead + try: + PowerOff=False + threshold = resource.get_attribute('PowerOff Threshold') + if int(threshold) <= 4 and int(threshold) > 0: + threshold = 0 + self.sandbox.report_info("Using power threshold of " + str(threshold)) + if int(threshold) > 4: + period_start = (datetime.datetime.now() + datetime.timedelta(minutes=2)).isoformat() + period_end = (datetime.datetime.now() + datetime.timedelta(minutes=int(threshold))).isoformat() + upcoming = resource.get_upcoming(resource.name, period_start=period_start, period_end=period_end) + for element in upcoming.Resources: + if element.Name == resource.name: + if element.Reservations.__len__() > 0: + self.sandbox.report_info(resource.name + ": not powered off due to upcoming use.", + write_to_output_window=True) + else: + PowerOff=True + if int(threshold) == 0 or PowerOff==True: + pwr_cmd = resource.has_connected_power_off() + if pwr_cmd > "": + resource.execute_connected_command(self.sandbox.id, pwr_cmd, tag='power') + self.sandbox.report_info(resource.name + ': powered OFF', + write_to_output_window=True) + else: + pwr_cmd = resource.has_power_off() + if pwr_cmd > "": + resource.execute_command(self.sandbox.id, pwr_cmd) + self.sandbox.report_info(resource.name + ': powered off', + write_to_output_window=True) + else: + self.sandbox.report_info(resource.name + ': no power_off command found', + write_to_output_window=True) + except Exception as ex: + print ex.message + return load_result # ---------------------------------- @@ -298,7 +360,8 @@ def _get_concrete_config_file_path(self, root_path, resource, config_stage, writ except: try: self.storage_mgr.download(tftp_template_config_path, tmp_template_config_file.name) - except: + except Exception as ex: + print ex.message # look for a generic config file for the model tftp_template_config_path = root_path + resource.model + '.tm' tftp_template_config_path = tftp_template_config_path.replace(' ', '_') @@ -454,6 +517,19 @@ def _is_load_config_to_device(self, resource, ignore_models=None): return True + def _is_power_ctrl_ok(self, resource, ignore_models=None): + if ignore_models: + for ignore_model in ignore_models: + if resource.model.lower() == ignore_model.lower(): + return False + + apps = self.sandbox.get_Apps_resources() + for app in apps: + if app.Name == resource.name: + return False + + return True + # ---------------------------------- # Are there devices in the reservation that need to be restored # ---------------------------------- From de5a8fe7b3d59df2ead37ffd6912efade2d7bdac Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Mon, 26 Jun 2017 10:40:49 -0700 Subject: [PATCH 03/50] changes for power control --- sandbox_scripts/environment/teardown/teardown_resources.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sandbox_scripts/environment/teardown/teardown_resources.py b/sandbox_scripts/environment/teardown/teardown_resources.py index 42e69f2..ab6e49c 100644 --- a/sandbox_scripts/environment/teardown/teardown_resources.py +++ b/sandbox_scripts/environment/teardown/teardown_resources.py @@ -18,8 +18,10 @@ def execute(self): sandbox = SandboxBase(self.reservation_id, self.logger) saveNRestoreTool = SaveRestoreManager(sandbox) - sandbox.report_info("Beginning load baseline configuration for resources", write_to_output_window=True) - sandbox.clear_all_resources_live_status() + sandbox.report_info("Beginning load baseline configuration for resources (teardown)", write_to_output_window=True) + ignore_models=['Generic TFTP server', 'Config Set Pool', 'Generic FTP server', + 'netscout switch 3912', 'Subnet-28', 'Subnet-30', 'GitLab'] + sandbox.clear_all_resources_live_status(ignore_models) try: if saveNRestoreTool.get_storage_manager(): ignore_models = ['Generic TFTP server', 'Config Set Pool', 'Generic FTP server', 'netscout switch 3912', From 246525a725507ea2d40571d94815c25d76bce8a8 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Mon, 26 Jun 2017 10:41:11 -0700 Subject: [PATCH 04/50] changes for adding power control --- sandbox_scripts/environment/setup/setup_resources.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sandbox_scripts/environment/setup/setup_resources.py b/sandbox_scripts/environment/setup/setup_resources.py index b01a7c8..8fc68f3 100644 --- a/sandbox_scripts/environment/setup/setup_resources.py +++ b/sandbox_scripts/environment/setup/setup_resources.py @@ -18,8 +18,11 @@ def execute(self): sandbox = SandboxBase(self.reservation_id, self.logger) saveNRestoreTool = SaveRestoreManager(sandbox) sandbox.report_info('Beginning load configuration for resources') + #Consider an ignore family capability? This list gets to be a maint issue...? + ignore_models=['Generic TFTP server', 'Config Set Pool', 'Generic FTP server', + 'netscout switch 3912', 'Subnet-28', 'Subnet-30', 'GitLab'] try: - sandbox.clear_all_resources_live_status() + sandbox.clear_all_resources_live_status(ignore_models) if sandbox.get_storage_server_resource(): # Get the config set name from the orchestration's params config_set_name = '' @@ -27,9 +30,7 @@ def execute(self): config_set_name = os.environ['Set Name'] except: pass - #Consider an ignore family capability? This list gets to be a maint issue...? - ignore_models=['Generic TFTP server', 'Config Set Pool', 'Generic FTP server', - 'netscout switch 3912', 'Subnet-28', 'Subnet-30', 'GitLab'] + if saveNRestoreTool.get_storage_manager(): if saveNRestoreTool.is_snapshot(): From a9c30b2385625c1d4c984e5a346c5e65e14c258b Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Mon, 26 Jun 2017 10:41:39 -0700 Subject: [PATCH 05/50] changes for power control and reset of live status to honor ignore models --- sandbox_scripts/QualiEnvironmentUtils/Sandbox.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py b/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py index 28415bb..9a76552 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py +++ b/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py @@ -240,15 +240,15 @@ def get_root_networking_resources(self): # ---------------------------------- # ---------------------------------- - def clear_all_resources_live_status(self): + def clear_all_resources_live_status(self,ignore_models): """ - Clear the live status from all the devices + Clear the live status from all the devices if not a model in ignore models """ - #TODO change to honor ignor_models root_resources = self.get_root_resources() for resource in root_resources: - self.api_session.SetResourceLiveStatus(resource.name, liveStatusName="Info", - additionalInfo='status cleared ' + strftime("%H:%M:%S", gmtime())) + if resource.model not in ignore_models: + self.api_session.SetResourceLiveStatus(resource.name, liveStatusName="Info", + additionalInfo='status cleared ' + strftime("%H:%M:%S", gmtime())) # ---------------------------------- # ---------------------------------- From a613c0fd7b7f48ce4e51ba3b79047705332ffb86 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Mon, 26 Jun 2017 10:42:03 -0700 Subject: [PATCH 06/50] changes to support functions for power control --- .../QualiEnvironmentUtils/Resource.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/sandbox_scripts/QualiEnvironmentUtils/Resource.py b/sandbox_scripts/QualiEnvironmentUtils/Resource.py index b3a9f2a..08a7a99 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/Resource.py +++ b/sandbox_scripts/QualiEnvironmentUtils/Resource.py @@ -29,6 +29,9 @@ def __init__(self, resource_name, resource_alias=''): self.alias = resource_alias + self.power_on_wl = ["power_on", "Power On", "Power ON", "PowerOn"] # whitelist for power on commands + self.power_off_wl = ["power_off", "Power Off", "Power OFF", "PowerOff"] # whitelist for power off commands + # ----------------------------------------- # ----------------------------------------- def has_command(self, command_name): @@ -40,6 +43,38 @@ def has_command(self, command_name): return True return False + # ----------------------------------------- + # ----------------------------------------- + def has_power_on(self): + for command in self.commands: + if command.Name in self.power_on_wl: + return command.Name + return "" + + # ----------------------------------------- + # ----------------------------------------- + def has_power_off(self): + for command in self.commands: + if command.Name in self.power_off_wl: + return command.Name + return "" + + # ----------------------------------------- + # ----------------------------------------- + def has_connected_power_on(self): + for command in self.connected_commands: + if command.Name in self.power_on_wl: + return command.Name + return "" + + # ----------------------------------------- + # ----------------------------------------- + def has_connected_power_off(self): + for command in self.connected_commands: + if command.Name in self.power_off_wl: + return command.Name + return "" + # ----------------------------------------- # ----------------------------------------- def attribute_exist(self, attribute_name): @@ -77,6 +112,19 @@ def set_attribute_value(self, attribute_name, attribute_value): except CloudShellAPIError as error: raise QualiError(self.name, "Failed to set attribute named or ending-with '" + attribute_name + "'. " + error.message) + # ----------------------------------------- + # ----------------------------------------- + def get_upcoming(self, dev_name, period_start, period_end): + try: + upcoming = self.api_session.GetResourceAvailabilityInTimeRange(resourcesNames=([dev_name]), + startTime=period_start, + endTime=period_end, + showAllDomains=False) + except CloudShellAPIError as error: + raise QualiError(self.name, "Failed to get upcoming rsvn list. " + error.message) + + return upcoming + # ----------------------------------------- # implement the command to get the neighbors and their ports # will return a dictionary of device's name and its port From 9ba9e245927855f20c2410362d1a2935057bb1c7 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Wed, 28 Jun 2017 11:16:11 -0700 Subject: [PATCH 07/50] allows for default on ignore models when clearing status --- sandbox_scripts/QualiEnvironmentUtils/Sandbox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py b/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py index 9a76552..75cc20a 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py +++ b/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py @@ -240,7 +240,7 @@ def get_root_networking_resources(self): # ---------------------------------- # ---------------------------------- - def clear_all_resources_live_status(self,ignore_models): + def clear_all_resources_live_status(self, ignore_models=[]): """ Clear the live status from all the devices if not a model in ignore models """ From 009f9ca8eca7f3e5b0b860ea248e2eeb2f8677dd Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Wed, 28 Jun 2017 11:16:36 -0700 Subject: [PATCH 08/50] tests updated to handle use of ignore models on clearing status --- .../QualiEnvironmentUtils/tests/test_Sandbox.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sandbox_scripts/QualiEnvironmentUtils/tests/test_Sandbox.py b/sandbox_scripts/QualiEnvironmentUtils/tests/test_Sandbox.py index e31febf..086b8dd 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/tests/test_Sandbox.py +++ b/sandbox_scripts/QualiEnvironmentUtils/tests/test_Sandbox.py @@ -210,6 +210,23 @@ def test_clear_all_resources_live_status_two_devices(self,mock_resourcebase): call('r2', additionalInfo='status cleared 12:00:01', liveStatusName='Info')] self.mock_api_session.return_value.SetResourceLiveStatus.assert_has_calls(calls) + @patch('sandbox_scripts.QualiEnvironmentUtils.Sandbox.ResourceBase') + def test_clear_all_resources_live_status_two_devices(self,mock_resourcebase): + resource1 = Mock() + resource1.name = "r1" + resource1.model = "m1" + resource2 = Mock() + resource2.name = "r2" + resource2.model = "m2" + rr = Mock() + rr = [resource1, resource2] + self.sandbox.get_root_resources = Mock(return_value=rr) + + with freeze_time("2017-01-17 12:00:01"): + self.sandbox.clear_all_resources_live_status(ignore_models=['m1']) + calls = [call('r2', additionalInfo='status cleared 12:00:01', liveStatusName='Info')] + self.mock_api_session.return_value.SetResourceLiveStatus.assert_has_calls(calls) + #================================================================ #test activate_connectors def test_activate_connectors_no_connectors_no_output(self): From f111875305c290c00eb70e059500b678fbcf4132 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 20 Jul 2017 14:51:03 -0700 Subject: [PATCH 09/50] Changes to get Save Sandpshot and Restore Snapshot to work. Save reasonable tested, Restore close - blocked - more testing ahead. Also added model to ignore_models --- .../QualiEnvironmentUtils/tests/test_ConfigFileManager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sandbox_scripts/QualiEnvironmentUtils/tests/test_ConfigFileManager.py b/sandbox_scripts/QualiEnvironmentUtils/tests/test_ConfigFileManager.py index 3b5c528..0a80e39 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/tests/test_ConfigFileManager.py +++ b/sandbox_scripts/QualiEnvironmentUtils/tests/test_ConfigFileManager.py @@ -160,7 +160,7 @@ def test_attribute_from_other_resource_in_sandbox(self, mock_api_session): resource = ResourceBase(resource_name='myresource', resource_alias='OtherDevice') sandbox = Mock() - sandbox.get_root_networking_resources.return_value = [resource] + sandbox.get_all_resources.return_value = [resource] tmp_template_config_file_data = """{Device:OtherDevice:Pool1}""" @@ -187,7 +187,7 @@ def test_attribute_from_other_resource_in_sandbox_not_found(self, mock_api_sessi resource = ResourceBase(resource_name='myresource', resource_alias='OtherDevice') sandbox = Mock() - sandbox.get_root_networking_resources.return_value = [resource] + sandbox.get_all_resources.return_value = [resource] tmp_template_config_file_data = """{Device:OtherDevice:Pool123}""" @@ -218,7 +218,7 @@ def test_attribute_from_other_resource_in_sandbox_resource_not_found(self, mock_ resource = ResourceBase(resource_name='myresource', resource_alias='OtherDevice') sandbox = Mock() - sandbox.get_root_networking_resources.return_value = [resource] + sandbox.get_all_resources.return_value = [resource] tmp_template_config_file_data = """{Device:OtherDevice2:Pool1}""" From ec663573c1a0d787f70f137b34d2f74ddce7a412 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 20 Jul 2017 14:51:19 -0700 Subject: [PATCH 10/50] Changes to get Save Sandpshot and Restore Snapshot to work. Save reasonable tested, Restore close - blocked - more testing ahead. Also added model to ignore_models --- .../QualiEnvironmentUtils/ConfigFileManager.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py b/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py index 4c5ba43..3d97ea6 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py +++ b/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py @@ -63,17 +63,17 @@ def create_concrete_config_from_template(self, template_config_data, config_set_ # param_val = resource.get_attribute(param) concrete_config_data = concrete_config_data.replace(param, param_val) - # Replacement of params from types: {Device:ALIAS:Attribute_name} + # Replacement of params from types: {Device:ALIAS:Attribute_name} WHERE ALIAS is at any structure level it = re.finditer(r'\{Device:[^}]*\}', concrete_config_data, flags=re.IGNORECASE) - root_resources = None + the_resources = None for match in it: param = match.group() junk, sb_alias, alias_attribname = param.split(":") alias_attribname = alias_attribname.replace("}", "") concrete_name = '' - if root_resources is None: # fetch once the resources - root_resources = sandbox.get_root_networking_resources() - for resource in root_resources: + if the_resources is None: # fetch once the resources + the_resources = sandbox.get_all_resources() + for resource in the_resources: if resource.alias == sb_alias: concrete_name = resource.name if resource.attribute_exist(alias_attribname): From 95097082c14b18c860e7fadb9a9751e2ac3597af Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 20 Jul 2017 14:51:39 -0700 Subject: [PATCH 11/50] Changes to get Save Sandpshot and Restore Snapshot to work. Save reasonable tested, Restore close - blocked - more testing ahead. Also added model to ignore_models --- .../QualiEnvironmentUtils/Resource.py | 41 ++++--------------- 1 file changed, 9 insertions(+), 32 deletions(-) diff --git a/sandbox_scripts/QualiEnvironmentUtils/Resource.py b/sandbox_scripts/QualiEnvironmentUtils/Resource.py index 08a7a99..4193bda 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/Resource.py +++ b/sandbox_scripts/QualiEnvironmentUtils/Resource.py @@ -235,44 +235,26 @@ def save_network_config(self, reservation_id, config_path, config_type): :param config_type: StartUp or Running """ #TODO modify the function to identify the command name and its params (similar behavior as in load_network_config) - # Run executeCommand with the restore command and its params (ConfigPath,RestoreMethod) + # Run executeCommand for the Save command and its params try: - command_inputs = [InputNameValue('source_filename', str(config_type)), - InputNameValue('destination_host', str(config_path))] + command_inputs = [InputNameValue('folder_path', str(config_path)), + InputNameValue('configuration_type', str(config_type))] if self.attribute_exist('VRF Management Name'): vrf_name = self.get_attribute('VRF Management Name') if vrf_name !='': - command_inputs.append(InputNameValue('vrf', str(vrf_name))) + command_inputs.append(InputNameValue('vrf_management_name', str(vrf_name))) - config_name = self.execute_command(reservation_id, 'Save', + config_name = self.execute_command(reservation_id, 'save', commandInputs=command_inputs, - printOutput=True).Output + printOutput=False).Output #TODO check the output is the created file name return config_name + except QualiError as qerror: + raise QualiError(self.name, "Failed to save configuration: " + qerror.message) except: - try: - command_inputs = [InputNameValue('source_filename', str(config_type)), - InputNameValue('destination_host', str(config_path))] - - if self.attribute_exist('VRF Management Name'): - vrf_name = self.get_attribute('VRF Management Name') - if vrf_name !='': - command_inputs.append(InputNameValue('vrf', str(vrf_name))) - - config_name = self.execute_command(reservation_id, 'save', - commandInputs=command_inputs, - printOutput=True).Output - - #TODO check the output is the created file name - config_name = config_name.rstrip(',') - return config_name - - except QualiError as qerror: - raise QualiError(self.name, "Failed to save configuration: " + qerror.message) - except: - raise QualiError(self.name, "Failed to save configuration. Unexpected error:" + str(sys.exc_info()[0])) + raise QualiError(self.name, "Failed to save configuration. Unexpected error:" + str(sys.exc_info()[0])) # ----------------------------------------- # ----------------------------------------- @@ -334,24 +316,19 @@ def orchestration_save(self, reservation_id, config_path, config_type): config_name = None if self.is_app(): - for command in self.connected_commands: if 'orchestration_save' == command.Name: tag = command.Tag - config_name = self.execute_connected_command(reservation_id, 'orchestration_save', tag, commandInputs=['shallow',''], printOutput=False) - return config_name.Output else: if self.has_command('orchestration_save'): - config_name = self.execute_command(reservation_id, 'orchestration_save', commandInputs=[InputNameValue('custom_params', json_str), InputNameValue('mode','shallow')], printOutput=False) - return config_name.Output if config_name: From 0efc50a063689aaf3cdbb385e0732382ec022b7a Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 20 Jul 2017 14:51:53 -0700 Subject: [PATCH 12/50] Changes to get Save Sandpshot and Restore Snapshot to work. Save reasonable tested, Restore close - blocked - more testing ahead. Also added model to ignore_models --- .../QualiEnvironmentUtils/Sandbox.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py b/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py index 75cc20a..8a404f0 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py +++ b/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py @@ -187,6 +187,32 @@ def get_root_resources(self): return root_resources + # ---------------------------------- + #created 28 June so we have all alias names accessible for substitution + def get_all_resources(self): + """ + Get the resources + :rtype: list[ResourceBase] + """ + all_resources = [] + all_resources_names_dict = {} + details = self.get_details() + resources = details.ReservationDescription.Resources + topo_resources = details.ReservationDescription.TopologiesReservedResources + # Loop over all devices in the sandbox and add to a dictionary all root devices: + for resource in resources: + all_resources_names_dict[resource.Name] = 1 + + # instantiate a resource object for each root device + for a_resource_name in all_resources_names_dict.keys(): + a_resource_alias = '' + for topo_resource in topo_resources: + if topo_resource.Name == a_resource_name: + a_resource_alias = topo_resource.Alias + break + all_resources.append(ResourceBase(a_resource_name, a_resource_alias)) + + return all_resources # ---------------------------------- # ---------------------------------- @@ -386,6 +412,22 @@ def enqueue_command(self, commandName, commandInputs=[], printOutput=False): # ----------------------------------------- # ----------------------------------------- + def setcategorysnapshots(self, snapshot_name): + try: + snapshot_name = str("Snapshots/" + snapshot_name) + self.api_session.SetTopologyCategory(snapshot_name, "Snapshots") + except CloudShellAPIError as error: + err = "Failed to set category to Snapshots. " + error.message + self.report_error(error_message=err, raise_error=True, write_to_output_window=True) + + # ----------------------------------------- + # ----------------------------------------- + def update_description(self, description): + # No api exists yet for this feature + return + + # ----------------------------------------- + # ------------- --------------------------- def save_sandbox_as_blueprint(self, blueprint_name, write_to_output=True): try: # TODO - fullpath should be passed as a param to the function and not hard coded From af1d2854a65d5d247ca47e744ef3a28f527a9fff Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 20 Jul 2017 14:52:03 -0700 Subject: [PATCH 13/50] Changes to get Save Sandpshot and Restore Snapshot to work. Save reasonable tested, Restore close - blocked - more testing ahead. Also added model to ignore_models --- sandbox_scripts/QualiEnvironmentUtils/StorageManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sandbox_scripts/QualiEnvironmentUtils/StorageManager.py b/sandbox_scripts/QualiEnvironmentUtils/StorageManager.py index b2e4374..cac2263 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/StorageManager.py +++ b/sandbox_scripts/QualiEnvironmentUtils/StorageManager.py @@ -90,7 +90,7 @@ def upload(self, destination, source): # ---------------------------------- # ---------------------------------- def dir_exist(self, dir_name): - self.storage_client.dir_exist(dir_name) + return self.storage_client.dir_exist(dir_name) # ---------------------------------- # ---------------------------------- From 28ffea416bde333cb5e3811a74b700f532d4d4ed Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 20 Jul 2017 14:52:15 -0700 Subject: [PATCH 14/50] Changes to get Save Sandpshot and Restore Snapshot to work. Save reasonable tested, Restore close - blocked - more testing ahead. Also added model to ignore_models --- .../environment/savesnapshot/SaveSnapshot.py | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/sandbox_scripts/environment/savesnapshot/SaveSnapshot.py b/sandbox_scripts/environment/savesnapshot/SaveSnapshot.py index 80f073f..cd6b647 100644 --- a/sandbox_scripts/environment/savesnapshot/SaveSnapshot.py +++ b/sandbox_scripts/environment/savesnapshot/SaveSnapshot.py @@ -4,7 +4,8 @@ from sandbox_scripts.helpers.Networking.save_restore_mgr import SaveRestoreManager from cloudshell.core.logger.qs_logger import get_qs_logger from sandbox_scripts.QualiEnvironmentUtils.QualiUtils import QualiError - +import os, sys +import datetime class EnvironmentSaveSnapshot: def __init__(self): @@ -13,32 +14,39 @@ def __init__(self): log_group=self.reservation_id, log_category='EnvironmentCommands') - def execute(self): sandbox = SandboxBase(self.reservation_id, self.logger) saveNRestoreTool = SaveRestoreManager(sandbox) + ignore_models=['Generic TFTP server', 'Config Set Pool', 'Generic FTP server', 'netscout switch 3912', + 'Subnet-28', 'Subnet-30', 'GitLab', 'SSID_Pool' ] - sandbox.clear_all_resources_live_status() + # why do this: sandbox.clear_all_resources_live_status(ignore_models) try: - username = helpers.get_reservation_context_details().owner_user if sandbox.get_storage_server_resource(): - snapshot_name = sandbox.Blueprint_name+"_"+username+"_"+ os.environ['name'] + #take first part of blueprint name up to first hyphen, underscore, or space + snapshot_name = sandbox.Blueprint_name.replace('-', '_') + snapshot_name = snapshot_name.replace(' ', '_').split('_')[0] + "_" + snapshot_name += username + "_" + os.environ['name'] sandbox.save_sandbox_as_blueprint(snapshot_name) - - # replace spaces with _ in the snapshot's name - snapshot_name = snapshot_name.replace(' ', '_') - - saveNRestoreTool.save_config(snapshot_name=snapshot_name, config_type='running', - ignore_models=['Generic TFTP server', 'Config Set Pool','Generic FTP server', - 'netscout switch 3912']) + sandbox.setcategorysnapshots(snapshot_name) + sandbox.report_info("Blueprint " + snapshot_name + " created", write_to_output_window=True) + #future feature when we have the api to update descriptions + #description = Snapshot of created + #description = "Snapshot of " + sandbox.Blueprint_name + #description += " created " + datetime.datetime.now().strftime("%I:%M%p on %B %d, %Y") + #sandbox.update_description(description) + + saveNRestoreTool.save_config(snapshot_name=snapshot_name, config_type='Running', + ignore_models=ignore_models) else: sandbox.report_error("There is no storage resource (e.g. FTP) available in the reservation",True,True) except QualiError as qe: self.logger.error("Save snapshot failed. " + str(qe)) - except: - self.logger.error("Save snapshot. Unexpected error:" + str(sys.exc_info())) + except Exception as ex: + blob = ex.message + self.logger.error("Save snapshot. Unexpected error: " + str(sys.exc_info())) From d4b0d36cc4e33eac284bb94f1f2c157d4684b5d1 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 20 Jul 2017 14:52:42 -0700 Subject: [PATCH 15/50] Changes to get Save Sandpshot and Restore Snapshot to work. Save reasonable tested, Restore close - blocked - more testing ahead. Also added model to ignore_models --- sandbox_scripts/environment/teardown/teardown_resources.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sandbox_scripts/environment/teardown/teardown_resources.py b/sandbox_scripts/environment/teardown/teardown_resources.py index ab6e49c..b1c18c7 100644 --- a/sandbox_scripts/environment/teardown/teardown_resources.py +++ b/sandbox_scripts/environment/teardown/teardown_resources.py @@ -20,7 +20,7 @@ def execute(self): sandbox.report_info("Beginning load baseline configuration for resources (teardown)", write_to_output_window=True) ignore_models=['Generic TFTP server', 'Config Set Pool', 'Generic FTP server', - 'netscout switch 3912', 'Subnet-28', 'Subnet-30', 'GitLab'] + 'netscout switch 3912', 'Subnet-28', 'Subnet-30', 'GitLab', 'SSID_Pool'] sandbox.clear_all_resources_live_status(ignore_models) try: if saveNRestoreTool.get_storage_manager(): From 0424770b874cad4848a9f6d548ef5b660f694661 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 20 Jul 2017 14:52:51 -0700 Subject: [PATCH 16/50] Changes to get Save Sandpshot and Restore Snapshot to work. Save reasonable tested, Restore close - blocked - more testing ahead. Also added model to ignore_models --- .../Networking/NetworkingSaveNRestore.py | 55 ++++++++----------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py b/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py index 4a5dbf1..d327930 100644 --- a/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py +++ b/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py @@ -156,20 +156,18 @@ def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, try: config_path = '' with lock: - config_path = self._get_concrete_config_file_path(root_path, resource, config_stage, + if config_stage == 'Snapshots': + config_path = root_path + resource.name + '_' + resource.model + '.cfg' + self.sandbox.report_info("Using " + str(config_path)) + else: + config_path = self._get_concrete_config_file_path(root_path, resource, config_stage, write_to_output=False) - self.sandbox.report_info("Config path for " + resource.alias + ": " + str(config_path)) + self.sandbox.report_info("Config path for " + resource.alias + ": " + str(config_path)) if use_Config_file_path_attr: resource.set_attribute_value('Config file path', config_path) # TODO - Snapshots currently only restore configuration. We need to restore firmware as well if config_stage.lower() == 'snapshots': - if resource.has_command('orchestration_restore'): - dest_name = resource.name + '_' + resource.model + '_artifact.txt' - dest_name = dest_name.replace(' ', '-') - saved_artifact_info = self.storage_mgr.download_artifact_info(config_path, dest_name) - resource.orchestration_restore(self.sandbox.id, config_path, saved_artifact_info) - else: - resource.load_network_config(self.sandbox.id, config_path, 'Running', 'Override') + resource.load_network_config(self.sandbox.id, config_path, 'Running', 'Override') else: if len(images_path_dict) > 0: # check what the device FW version is currently. @@ -341,10 +339,7 @@ def _get_concrete_config_file_path(self, root_path, resource, config_stage, writ if config_set_pool_resource is not None: config_set_pool_manager = ConfigPoolManager(sandbox=self.sandbox, pool_resource=config_set_pool_resource) config_set_pool_data = config_set_pool_manager.pool_data - if config_stage == 'snapshots': - config_path = root_path + resource.name + '_' + resource.model + '.cfg' - else: - config_path = root_path + resource.alias + '_' + resource.model + '.cfg' + config_path = root_path + resource.alias + '_' + resource.model + '.cfg' config_path = config_path.replace(' ', '_') # Look for a template config file tmp_template_config_file = tempfile.NamedTemporaryFile(delete=False) @@ -415,10 +410,14 @@ def save_config(self, snapshot_name, config_type, ignore_models=None, write_to_o env_dir = "" try: env_dir = self.config_files_root + '/Snapshots/' + snapshot_name.strip() - if not self.storage_mgr.dir_exist(env_dir): + if self.storage_mgr.dir_exist(env_dir): + self.sandbox.report_info("Directory for snapshot already exists:\n" + env_dir, + write_to_output_window=True) + else: self.storage_mgr.create_dir(env_dir, write_to_output=True) + self.sandbox.report_info("Configs saving to\n" + env_dir, write_to_output_window=True) except QualiError as e: - self.sandbox.report_error("Save snapshot failed. " + str(e), + self.sandbox.report_error("Save snapshot failed attempting to make dir. " + str(e), write_to_output_window=write_to_output, raise_error=True) root_resources = self.sandbox.get_root_networking_resources() @@ -440,7 +439,7 @@ def save_config(self, snapshot_name, config_type, ignore_models=None, write_to_o self.sandbox.report_error(res.message, raise_error=False, send_email=True) elif res.message != '': - self.sandbox.report_info(res.resource_name + "\n" + res.message) + self.sandbox.report_info(res.message, write_to_output_window=True) # ---------------------------------- # ---------------------------------- @@ -452,23 +451,13 @@ def _run_asynch_save(self, resource, snapshot_dir, config_type, lock, ignore_mod save_config_for_device = self._is_load_config_to_device(resource, ignore_models=ignore_models) if save_config_for_device: try: - message += '\nSaving configuration for device: ' + resource.name - if resource.has_command('orchestration_save'): - config_path = snapshot_dir.replace('\\', '/') - saved_artifact_info = resource.orchestration_save(self.sandbox.id, config_path, config_type) - if saved_artifact_info != "": - dest_name = resource.name + '_' + resource.model + '_artifact.txt' - dest_name = dest_name.replace(' ', '-') - with lock: - self.storage_mgr.save_artifact_info(saved_artifact_info, config_path, dest_name, - write_to_output=True) - else: - file_name = resource.save_network_config(self.sandbox.id, snapshot_dir, config_type) - # rename file on the storage server - file_path = snapshot_dir + '/' + file_name - to_name = resource.name + '_' + resource.model + '.cfg' - with lock: - self.storage_mgr.rename_file(file_path, to_name) + file_name = resource.save_network_config(self.sandbox.id, snapshot_dir, config_type) + # rename file on the storage server + file_path = snapshot_dir + '/' + file_name + to_name = resource.name + '_' + resource.model + '.cfg' + with lock: + self.storage_mgr.rename_file(file_path, to_name) + message += 'Saved config for device: ' + resource.name except QualiError as qe: save_result.run_result = False From ca79008b4fba89ea4ba8d1206af738d0d7293543 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 20 Jul 2017 14:53:02 -0700 Subject: [PATCH 17/50] Changes to get Save Sandpshot and Restore Snapshot to work. Save reasonable tested, Restore close - blocked - more testing ahead. Also added model to ignore_models --- sandbox_scripts/helpers/Networking/save_restore_mgr.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sandbox_scripts/helpers/Networking/save_restore_mgr.py b/sandbox_scripts/helpers/Networking/save_restore_mgr.py index ac4c160..1552464 100644 --- a/sandbox_scripts/helpers/Networking/save_restore_mgr.py +++ b/sandbox_scripts/helpers/Networking/save_restore_mgr.py @@ -34,6 +34,8 @@ def save_config(self, snapshot_name, config_type, ignore_models=None, write_to_o self.networking_save_restore.save_config(snapshot_name=snapshot_name,config_type=config_type, ignore_models=ignore_models,write_to_output=write_to_output) + + self.vm_save_restore.save_config(snapshot_name=snapshot_name , config_type=config_type, ignore_models=ignore_models, write_to_output=write_to_output) From 977d10276b410b0a2b3ff13af880554a3fa1e34d Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 20 Jul 2017 14:53:13 -0700 Subject: [PATCH 18/50] Changes to get Save Sandpshot and Restore Snapshot to work. Save reasonable tested, Restore close - blocked - more testing ahead. Also added model to ignore_models --- .../helpers/Networking/vm_save_restore.py | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/sandbox_scripts/helpers/Networking/vm_save_restore.py b/sandbox_scripts/helpers/Networking/vm_save_restore.py index 3f7bac2..c3c12c4 100644 --- a/sandbox_scripts/helpers/Networking/vm_save_restore.py +++ b/sandbox_scripts/helpers/Networking/vm_save_restore.py @@ -124,8 +124,10 @@ def save_config(self, snapshot_name, config_type, ignore_models=None, write_to_o env_dir = "" try: - env_dir = self.config_files_root + '/Snapshots/' + snapshot_name.strip() - if not self.storage_mgr.dir_exist(env_dir): + env_dir = self.config_files_root + '/Snapshots/' + snapshot_name + if self.storage_mgr.dir_exist(env_dir): + pass + else: self.storage_mgr.create_dir(env_dir, write_to_output=True) except QualiError as e: self.sandbox.report_error("Save snapshot failed. " + str(e), @@ -133,24 +135,25 @@ def save_config(self, snapshot_name, config_type, ignore_models=None, write_to_o root_resources = self.sandbox.get_root_vm_resources() """:type : list[ResourceBase]""" - pool = ThreadPool(len(root_resources)) - lock = Lock() - async_results = [pool.apply_async(self._run_asynch_save, - (resource, env_dir, config_type, lock, ignore_models)) + if len(root_resources) == 0: + self.sandbox.report_info("No VMs found to snapshot.", write_to_output_window=True) + else: + pool = ThreadPool(len(root_resources)) + lock = Lock() + async_results = [pool.apply_async(self._run_asynch_save, + (resource, env_dir, config_type, lock, ignore_models)) for resource in root_resources] - - pool.close() - pool.join() - for async_result in async_results: - res = async_result.get() - """:type : rsc_run_result_struct""" - if not res.run_result: - err = "Failed to save configuration on device " + res.resource_name - self.sandbox.report_error(err, write_to_output_window=write_to_output, raise_error=False) - self.sandbox.report_error(res.message, raise_error=False) - - elif res.message != '': - self.sandbox.report_info(res.resource_name + "\n" + res.message) + pool.close() + pool.join() + for async_result in async_results: + res = async_result.get() + """:type : rsc_run_result_struct""" + if not res.run_result: + err = "Failed to save configuration on device " + res.resource_name + self.sandbox.report_error(err, write_to_output_window=write_to_output, raise_error=False) + self.sandbox.report_error(res.message, raise_error=False) + elif res.message != '': + self.sandbox.report_info(res.message, write_to_output_window=True) # ---------------------------------- # ---------------------------------- From e658a457b5e5ea196a0a86f3f4543072bd827c07 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 20 Jul 2017 14:54:14 -0700 Subject: [PATCH 19/50] Changes to get Save Sandpshot and Restore Snapshot to work. Save reasonable tested, Restore close - blocked - more testing ahead. Save requires blueprint category "Snapshots" exist and root dir "Snapshots" in resoruce manager. --- README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 09fdd07..3e6167c 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,8 @@ The orchestration package should bring an out-of-the-box solution for our custom ### Setup During the setup process the script will iterate over the resources and prepare them for the reservation. - For each networking device: - - Perform health check (if exists) + - Power up + - Perform health check (if exists) - Load firmware (optional) - Load configuration (optional) - Re-run health check (if exists) @@ -34,7 +35,12 @@ During the teardown process the script will wipe the configuration from the netw - Disconnect routes and connectors ### Snapshot -The user can save a snapshot of the sandbox. In the background, the script will save the sandbox as a new blueprint, and the current configuration of the devices and VMs will be saved for future use (reserving this saved blueprint will restore the configuration on the resources and the VM snapshots). -- Save the sandbox as a blueprint +The user can save a snapshot of the sandbox. In the background, the script will save the sandbox as a new blueprint, and +the current configuration of the devices and VMs will be saved for future use (reserving this saved blueprint will +restore the configuration on the resources and the VM snapshots). +- Save the sandbox as a blueprint, add to blueprint category "Snapshots" - Save all the configuration files of the devices on the storage server (e.g. FTP server) - +- REQUIRES + a) root folder "Snapshots" created in Resource Manager + b) Create a blueprint category "Snapshots" + c) On ftp storage, create ftproot/CloudShell/Snapshots From 4672343666241a41a4c23c86c4e9dda217686ba1 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Mon, 24 Jul 2017 09:25:03 -0700 Subject: [PATCH 20/50] Minor changes to resolve save snapshot/restore --- sandbox_scripts/environment/setup/setup_resources.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sandbox_scripts/environment/setup/setup_resources.py b/sandbox_scripts/environment/setup/setup_resources.py index 8fc68f3..4e8f9b6 100644 --- a/sandbox_scripts/environment/setup/setup_resources.py +++ b/sandbox_scripts/environment/setup/setup_resources.py @@ -20,7 +20,7 @@ def execute(self): sandbox.report_info('Beginning load configuration for resources') #Consider an ignore family capability? This list gets to be a maint issue...? ignore_models=['Generic TFTP server', 'Config Set Pool', 'Generic FTP server', - 'netscout switch 3912', 'Subnet-28', 'Subnet-30', 'GitLab'] + 'netscout switch 3912', 'Subnet-28', 'Subnet-30', 'GitLab', 'SSID_Pool' ] try: sandbox.clear_all_resources_live_status(ignore_models) if sandbox.get_storage_server_resource(): @@ -31,7 +31,6 @@ def execute(self): except: pass - if saveNRestoreTool.get_storage_manager(): if saveNRestoreTool.is_snapshot(): saveNRestoreTool.load_config(config_stage='Snapshots', config_type='Running', From 71f53c062e771accd265eef77e514c9a3ff592bb Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Mon, 24 Jul 2017 09:25:11 -0700 Subject: [PATCH 21/50] Minor changes to resolve save snapshot/restore --- sandbox_scripts/helpers/Networking/vm_save_restore.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sandbox_scripts/helpers/Networking/vm_save_restore.py b/sandbox_scripts/helpers/Networking/vm_save_restore.py index c3c12c4..292f3a1 100644 --- a/sandbox_scripts/helpers/Networking/vm_save_restore.py +++ b/sandbox_scripts/helpers/Networking/vm_save_restore.py @@ -36,12 +36,11 @@ def load_config(self, config_stage, config_set_name='', ignore_models=None, root_path = root_path + config_set_name.strip() + '/' root_path = root_path.replace(' ', '_') - - self.sandbox.report_info( - "Loading image on the VMs. This action may take some time.",write_to_output_window=True) root_resources = self.sandbox.get_root_vm_resources() """:type : list[ResourceBase]""" if len(root_resources) > 0: + self.sandbox.report_info("Loading image on the VMs. This action may take some time.", + write_to_output_window=True) pool = ThreadPool(len(root_resources)) async_results = [pool.apply_async(self._run_asynch_load, (resource, root_path, ignore_models, in_teardown_mode)) @@ -60,7 +59,7 @@ def load_config(self, config_stage, config_set_name='', ignore_models=None, elif res.message != '': self.sandbox.report_info(res.resource_name + "\n" + res.message) else: - self.sandbox.report_info("No VM resources found to process") + self.sandbox.report_info("No VM resources found to process", write_to_output_window=True) # ---------------------------------- # ---------------------------------- From 94a99be4465aac943db3a26ea0e7478f3aae58d9 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Tue, 25 Jul 2017 10:18:57 -0700 Subject: [PATCH 22/50] minor imprpovement to the power off at teardown --- sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py b/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py index d327930..b2a561a 100644 --- a/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py +++ b/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py @@ -250,7 +250,7 @@ def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, load_result.message = message - if is_power_ctrl_ok and in_teardown_mode: + if is_power_ctrl_ok and in_teardown_mode and resource.model not in ignore_models: # Attempt PowerOFF. # If threshold is a Negative value, no power off. # If threshold is 0-4, power off now @@ -258,9 +258,10 @@ def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, try: PowerOff=False threshold = resource.get_attribute('PowerOff Threshold') + # avoid attempts to manage too small a window if int(threshold) <= 4 and int(threshold) > 0: threshold = 0 - self.sandbox.report_info("Using power threshold of " + str(threshold)) + self.sandbox.report_info("Using power threshold of " + str(threshold) + " for " + resource.name) if int(threshold) > 4: period_start = (datetime.datetime.now() + datetime.timedelta(minutes=2)).isoformat() period_end = (datetime.datetime.now() + datetime.timedelta(minutes=int(threshold))).isoformat() From 0299f9d29232faa5474432f4a53610b33152baf3 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Tue, 25 Jul 2017 11:26:58 -0700 Subject: [PATCH 23/50] clarified error - which resource it applies to --- sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py b/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py index 3d97ea6..a3e8cdc 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py +++ b/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py @@ -88,5 +88,5 @@ def create_concrete_config_from_template(self, template_config_data, config_set_ return concrete_config_data except Exception as ex: - raise QualiError('ConfigFileManager', "Failed to create a concrete config file from the template\'s data. " - "Unexpected error: " + ex.message) + raise QualiError('ConfigFileManager', "Failed to create concrete config for " + resource.name + + " from template. Unexpected error: " + ex.message) \ No newline at end of file From 6c8eb2304a1c2f5699cacfba235d8d9b48b8dc8b Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Tue, 25 Jul 2017 11:31:02 -0700 Subject: [PATCH 24/50] mssing newline at EOF --- sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py b/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py index a3e8cdc..b4be577 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py +++ b/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py @@ -89,4 +89,4 @@ def create_concrete_config_from_template(self, template_config_data, config_set_ return concrete_config_data except Exception as ex: raise QualiError('ConfigFileManager', "Failed to create concrete config for " + resource.name + - " from template. Unexpected error: " + ex.message) \ No newline at end of file + " from template. Unexpected error: " + ex.message) From 94a8e5753e0858c60170b07cf296664eca3eb976 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Mon, 14 Aug 2017 12:50:25 -0700 Subject: [PATCH 25/50] Added SSIB Model to ignore models. Ignore model should be changed to get its value from a global poool resource attrib!?!?!?! --- sandbox_scripts/environment/teardown/teardown_resources.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sandbox_scripts/environment/teardown/teardown_resources.py b/sandbox_scripts/environment/teardown/teardown_resources.py index b1c18c7..cfd34ed 100644 --- a/sandbox_scripts/environment/teardown/teardown_resources.py +++ b/sandbox_scripts/environment/teardown/teardown_resources.py @@ -4,7 +4,7 @@ from sandbox_scripts.QualiEnvironmentUtils.Sandbox import SandboxBase from sandbox_scripts.helpers.Networking.save_restore_mgr import SaveRestoreManager from sandbox_scripts.QualiEnvironmentUtils.QualiUtils import QualiError - +import sys class EnvironmentTeardownResources: def __init__(self): @@ -24,8 +24,9 @@ def execute(self): sandbox.clear_all_resources_live_status(ignore_models) try: if saveNRestoreTool.get_storage_manager(): - ignore_models = ['Generic TFTP server', 'Config Set Pool', 'Generic FTP server', 'netscout switch 3912', - 'OnPATH Switch 3903', 'Ixia Traffic generator', "SubNet-28", "SubNet-30", "GitLab"] + ignore_models = ['Generic TFTP server', 'Config Set Pool', 'Generic FTP server', + 'netscout switch 3912', 'OnPATH Switch 3903', 'Ixia Traffic generator', + "SubNet-28", "SubNet-30", "GitLab", 'SSID_Pool'] saveNRestoreTool.load_config(config_stage='Base', config_type='Running', ignore_models=ignore_models, From c97d940ba380faf4aa3ff79223c4cc79c734aea8 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Mon, 14 Aug 2017 12:53:27 -0700 Subject: [PATCH 26/50] added newline at end --- sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py b/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py index b4be577..97226a7 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py +++ b/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py @@ -90,3 +90,4 @@ def create_concrete_config_from_template(self, template_config_data, config_set_ except Exception as ex: raise QualiError('ConfigFileManager', "Failed to create concrete config for " + resource.name + " from template. Unexpected error: " + ex.message) + From e84d954a786bec43095589dfea5f1db37a6a33eb Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Tue, 5 Sep 2017 09:49:05 -0700 Subject: [PATCH 27/50] Changes to handling invalid name appendage to save snapshot. Command should fail, not succeed and error needed to be raised. --- sandbox_scripts/environment/savesnapshot/SaveSnapshot.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sandbox_scripts/environment/savesnapshot/SaveSnapshot.py b/sandbox_scripts/environment/savesnapshot/SaveSnapshot.py index cd6b647..a2256bf 100644 --- a/sandbox_scripts/environment/savesnapshot/SaveSnapshot.py +++ b/sandbox_scripts/environment/savesnapshot/SaveSnapshot.py @@ -44,8 +44,10 @@ def execute(self): sandbox.report_error("There is no storage resource (e.g. FTP) available in the reservation",True,True) except QualiError as qe: - self.logger.error("Save snapshot failed. " + str(qe)) - + self.logger.error("Save snapshot failed. " + qe.message) + if "Name must contain only al" in qe.message: + sys.tracebacklimit = 0 + raise Exception("Save snapshot failed. " + qe.message) except Exception as ex: blob = ex.message self.logger.error("Save snapshot. Unexpected error: " + str(sys.exc_info())) From 33a9de6ca3ca3280db6876e5a882c6d3fae55002 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Tue, 5 Sep 2017 09:50:43 -0700 Subject: [PATCH 28/50] Removed teardown premature message --- sandbox_scripts/environment/teardown/teardown_script.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sandbox_scripts/environment/teardown/teardown_script.py b/sandbox_scripts/environment/teardown/teardown_script.py index c3bc12c..39b67a1 100644 --- a/sandbox_scripts/environment/teardown/teardown_script.py +++ b/sandbox_scripts/environment/teardown/teardown_script.py @@ -33,8 +33,8 @@ def execute(self): self._cleanup_connectivity(api, self.reservation_id) self.logger.info("Teardown for reservation {0} completed".format(self.reservation_id)) - api.WriteMessageToReservationOutput(reservationId=self.reservation_id, - message='Sandbox teardown finished successfully') + #api.WriteMessageToReservationOutput(reservationId=self.reservation_id, + # message='Sandbox teardown finished successfully') def _disconnect_all_routes_in_reservation(self, api, reservation_details): connectors = reservation_details.ReservationDescription.Connectors From 855328d946bb0b096c7d3904b07a10b9a41ec6db Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Tue, 5 Sep 2017 09:51:30 -0700 Subject: [PATCH 29/50] Added msg that clear status indicatores is running - else the user ponders what the heck is going on! --- sandbox_scripts/environment/setup/setup_resources.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sandbox_scripts/environment/setup/setup_resources.py b/sandbox_scripts/environment/setup/setup_resources.py index 4e8f9b6..e9a25f3 100644 --- a/sandbox_scripts/environment/setup/setup_resources.py +++ b/sandbox_scripts/environment/setup/setup_resources.py @@ -18,6 +18,7 @@ def execute(self): sandbox = SandboxBase(self.reservation_id, self.logger) saveNRestoreTool = SaveRestoreManager(sandbox) sandbox.report_info('Beginning load configuration for resources') + sandbox.report_info('Clearing status indicators ', write_to_output_window=True) #Consider an ignore family capability? This list gets to be a maint issue...? ignore_models=['Generic TFTP server', 'Config Set Pool', 'Generic FTP server', 'netscout switch 3912', 'Subnet-28', 'Subnet-30', 'GitLab', 'SSID_Pool' ] From 98683dd567146bc7a2d22cea9af336128315b38b Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Tue, 5 Sep 2017 09:54:16 -0700 Subject: [PATCH 30/50] changes handling error if firmware file has a line with incomplete entries handling power-off wasn't working correct -fixed misc msg cleanup --- .../Networking/NetworkingSaveNRestore.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py b/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py index b2a561a..1532181 100644 --- a/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py +++ b/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py @@ -148,6 +148,8 @@ def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, print resource.name + " power on fault. " + ex.message load_config_to_device = self._is_load_config_to_device(resource, ignore_models=ignore_models) + # ------------------------------------------------------ + #if load_config_to_device and '3850-6' in resource.name: if load_config_to_device: self.sandbox.report_info(resource.name + " - starting health check", write_to_output_window=True) health_check_result = resource.health_check(self.sandbox.id, health_check_attempts) @@ -191,9 +193,11 @@ def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, if image_key: #self.sandbox.report_info("Using image key " + str(image_key) + " for " + resource.name) dict_img_version = images_path_dict[image_key].version + if version < ' ': + self.sandbox.report_error('Firmware spec file is missing version element for ' + resource.name) else: # Getting here means no firmware specified in FirmwareData.csv - message += "\n" + resource.name + ": NO firmware version specified in Base FirmwareData.csv" + message += "\n" + resource.name + ": NO firmware version specified in FirmwareData.csv" # same image version - Only load config (running override) #message += resource.name + ": loading configuration from " + config_path @@ -222,12 +226,12 @@ def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, message += "\n" + resource.name + ": loading config from:" + config_path resource.load_network_config(self.sandbox.id, config_path, 'Running', 'Override') - health_check_result = resource.health_check(self.sandbox.id, health_check_attempts=1) + health_check_result = resource.health_check(self.sandbox.id, health_check_attempts=1, printOutput=False) if health_check_result != '': raise QualiError(self.sandbox.id, resource.name + " did not pass health check after loading configuration") else: - self.sandbox.report_info(resource.name + " -- final Health Check Passed.") + self.sandbox.report_info(resource.name + " - Final Health Check Passed.", write_to_output_window=True) except QualiError as qe: load_result.run_result = False @@ -256,7 +260,7 @@ def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, # If threshold is 0-4, power off now # If threshold is >5, check for usesage in that period with a 2minute shift ahead try: - PowerOff=False + PowerOff = True threshold = resource.get_attribute('PowerOff Threshold') # avoid attempts to manage too small a window if int(threshold) <= 4 and int(threshold) > 0: @@ -269,10 +273,11 @@ def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, for element in upcoming.Resources: if element.Name == resource.name: if element.Reservations.__len__() > 0: - self.sandbox.report_info(resource.name + ": not powered off due to upcoming use.", + for resv in element.Reservations: + if resv.StartTime >= period_start and resv.StartTime < period_end: + self.sandbox.report_info(resource.name + ": not powered off due to upcoming use.", write_to_output_window=True) - else: - PowerOff=True + PowerOff = False if int(threshold) == 0 or PowerOff==True: pwr_cmd = resource.has_connected_power_off() if pwr_cmd > "": From e1c83a19ab72028c206e1cc20323eec56fd27ab2 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Tue, 5 Sep 2017 11:02:57 -0700 Subject: [PATCH 31/50] updated to replace a space in the user-add-on-name with an underscore else copy command would fail on device --- sandbox_scripts/environment/savesnapshot/SaveSnapshot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sandbox_scripts/environment/savesnapshot/SaveSnapshot.py b/sandbox_scripts/environment/savesnapshot/SaveSnapshot.py index a2256bf..5f93c54 100644 --- a/sandbox_scripts/environment/savesnapshot/SaveSnapshot.py +++ b/sandbox_scripts/environment/savesnapshot/SaveSnapshot.py @@ -28,7 +28,8 @@ def execute(self): #take first part of blueprint name up to first hyphen, underscore, or space snapshot_name = sandbox.Blueprint_name.replace('-', '_') snapshot_name = snapshot_name.replace(' ', '_').split('_')[0] + "_" - snapshot_name += username + "_" + os.environ['name'] + snapshot_name += username + "_" + os.environ['name'].replace(' ','_') + snapshot_name = snapshot_name.replace('-', '_') sandbox.save_sandbox_as_blueprint(snapshot_name) sandbox.setcategorysnapshots(snapshot_name) sandbox.report_info("Blueprint " + snapshot_name + " created", write_to_output_window=True) From a8bc1380049cf6f9396cdb93e5b4903fb5f87b8f Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Tue, 5 Sep 2017 16:25:39 -0700 Subject: [PATCH 32/50] made changes to health check so that the orchestration controls the content of the final health check - makes it easier to track. Added replace in init to handle special case of a very long resource model attrib value. --- sandbox_scripts/QualiEnvironmentUtils/Resource.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sandbox_scripts/QualiEnvironmentUtils/Resource.py b/sandbox_scripts/QualiEnvironmentUtils/Resource.py index 4193bda..fe6d04b 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/Resource.py +++ b/sandbox_scripts/QualiEnvironmentUtils/Resource.py @@ -26,7 +26,9 @@ def __init__(self, resource_name, resource_alias=''): self.model = self.get_attribute('Model') else: self.model = self.details.ResourceModelName - + #self.model = self.model.replace(' ','_') + #Hacked in to cover for filenames too long for product, such as those below: + self.model = self.model.replace('Cisco 5500 Series Wireless LAN Controller','WLC') self.alias = resource_alias self.power_on_wl = ["power_on", "Power On", "Power ON", "PowerOn"] # whitelist for power on commands @@ -144,7 +146,7 @@ def get_neighbors(self, reservation_id): # ---------------------------------- # ---------------------------------- - def health_check(self,reservation_id, health_check_attempts=1): + def health_check(self,reservation_id, health_check_attempts=1, printOutput=True): """ Run the healthCheck command on the device :param str reservation_id: Reservation id. @@ -153,7 +155,7 @@ def health_check(self,reservation_id, health_check_attempts=1): for attempts in range(0, int(health_check_attempts)): try: # Return a detailed description in case of a failure - out = self.execute_command(reservation_id, 'health_check', printOutput=True) #.Output() + out = self.execute_command(reservation_id, 'health_check', printOutput=printOutput) #.Output() if out.Output.find(' passed') == -1 and attempts == (int(health_check_attempts) -1): err = "Health check did not pass for device " + self.name + ". " + out.Output return err From 1f60e3fbd2a9402e68e9e500391791da515ab93a Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Tue, 5 Sep 2017 16:26:37 -0700 Subject: [PATCH 33/50] just two format cleanup items as pycharm was complaining --- sandbox_scripts/QualiEnvironmentUtils/StorageManager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sandbox_scripts/QualiEnvironmentUtils/StorageManager.py b/sandbox_scripts/QualiEnvironmentUtils/StorageManager.py index cac2263..2954cbf 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/StorageManager.py +++ b/sandbox_scripts/QualiEnvironmentUtils/StorageManager.py @@ -26,7 +26,7 @@ def __init__(self, sandbox ): # ---------------------------------- # ---------------------------------- - #def _get_storage_client(self, storage_resource): + # def _get_storage_client(self, storage_resource): # if storage_resource.model.lower() == 'generic tftp server': # return TFTPClient(self.sandbox,storage_resource) # elif storage_resource.model.lower() == 'generic ftp server': @@ -38,7 +38,7 @@ def _get_storage_client(self, storage_resource): elif storage_resource.model.lower() == 'generic ftp server': return FTPClient(self.sandbox,storage_resource) - #---------------------------------- + # ----------------------------------- # ---------------------------------- def _get_repository_client(self, repository_resource): if repository_resource.model.lower() == 'gitlab': From 04369f91d89d08a51a668a35135503adba447df8 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Wed, 27 Sep 2017 11:02:50 -0700 Subject: [PATCH 34/50] Added a subst_log variable that is passed in case it is needed in future debugging - found it helpful but it is never writen to log -just for interactve use in debug for now --- .../QualiEnvironmentUtils/ConfigFileManager.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py b/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py index 97226a7..502f14a 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py +++ b/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py @@ -24,12 +24,14 @@ def create_concrete_config_from_template(self, template_config_data, config_set_ """ try: concrete_config_data = template_config_data + subst_log = "" # Replace {ConfigPool.PARAM} with PARAM's value from the pool it = re.finditer(r'\{ConfigPool\:[^}]*\}', concrete_config_data, flags=re.IGNORECASE) for match in it: param = match.group() if param.lower() in config_set_pool_data: concrete_config_data = concrete_config_data.replace(param, config_set_pool_data[param.lower()]) + subst_log += param + ' = ' + config_set_pool_data[param.lower()] + '\n' else: raise Exception('Could not find attribute ' + param.lower() + ' in the config pool') @@ -39,19 +41,19 @@ def create_concrete_config_from_template(self, template_config_data, config_set_ param = match.group() quali_note = 'Built from template: ' + strftime('%Y-%b-%d %H:%M:%S', gmtime()) concrete_config_data = concrete_config_data.replace(param, quali_note) - + subst_log += param + ' = ' + quali_note + '\n' # Replace {Device.Self.Name} with the resource's name it = re.finditer(r'\{Device:Self:Name\}', concrete_config_data, flags=re.IGNORECASE) for match in it: param = match.group() concrete_config_data = concrete_config_data.replace(param, resource.name) - + subst_log += param + ' = ' + resource.name + '\n' # Replace {Device.Self.Address} with the resource's management ip it = re.finditer(r'\{Device:Self:Address\}', concrete_config_data, flags=re.IGNORECASE) for match in it: param = match.group() concrete_config_data = concrete_config_data.replace(param, resource.address) - + subst_log += param + ' = ' + resource.address + '\n' # Replace {Device.Self.ATTRIBUTE_NAME} with the resource's attribute value # TODO: Need to decode password attributes: Password, Enable Password, and SNMP Read Community it = re.finditer(r'\{Device:Self\:[^}]*\}', concrete_config_data, flags=re.IGNORECASE) @@ -62,7 +64,7 @@ def create_concrete_config_from_template(self, template_config_data, config_set_ param_val = resource.get_attribute(att_name) # param_val = resource.get_attribute(param) concrete_config_data = concrete_config_data.replace(param, param_val) - + subst_log += param + ' = ' + param_val + '\n' # Replacement of params from types: {Device:ALIAS:Attribute_name} WHERE ALIAS is at any structure level it = re.finditer(r'\{Device:[^}]*\}', concrete_config_data, flags=re.IGNORECASE) the_resources = None @@ -82,11 +84,12 @@ def create_concrete_config_from_template(self, template_config_data, config_set_ raise Exception("Could not find attribute '{0}' in resource '{1}'".format(alias_attribname, resource.name)) concrete_config_data = concrete_config_data.replace(param, param_val) + subst_log += param + ' = ' + param_val + '\n' break if concrete_name <= ' ': raise Exception('Could not find a resource with alias ' + sb_alias + '; likely missing from blueprint.') - return concrete_config_data + return concrete_config_data, subst_log except Exception as ex: raise QualiError('ConfigFileManager', "Failed to create concrete config for " + resource.name + " from template. Unexpected error: " + ex.message) From fb36872445ddea187a50866917fc645dfd37fb50 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Wed, 27 Sep 2017 11:04:26 -0700 Subject: [PATCH 35/50] Changed ending message - removed Successfully as it is not always the case --- sandbox_scripts/environment/setup/setup_resources.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sandbox_scripts/environment/setup/setup_resources.py b/sandbox_scripts/environment/setup/setup_resources.py index e9a25f3..77ee302 100644 --- a/sandbox_scripts/environment/setup/setup_resources.py +++ b/sandbox_scripts/environment/setup/setup_resources.py @@ -51,7 +51,7 @@ def execute(self): # call activate_all_routes_and_connectors sandbox.activate_all_routes_and_connectors() - sandbox.report_info('Sandbox setup finished successfully') + sandbox.report_info('Sandbox setup finished.', write_to_output_window=True) # Call routes_validation # sandbox.routes_validation() From 8ce48b027398bcc7914d847f8af7cbbb2be3f5fc Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Wed, 27 Sep 2017 11:08:02 -0700 Subject: [PATCH 36/50] Fixes in a number of places where we did not account for a space in a resource model name. Added handling of Subst_log variable - if a resource has the attribute, the substitution log info is written to the attribute. This contains the info on what substitutions were done to change the template to a config. --- .../Networking/NetworkingSaveNRestore.py | 47 +++++++++++++++---- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py b/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py index 1532181..31d2c5f 100644 --- a/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py +++ b/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py @@ -1,6 +1,6 @@ # coding=utf-8 import csv -import os, datetime +import os, datetime, time import tempfile from multiprocessing.pool import ThreadPool from threading import Lock @@ -9,6 +9,7 @@ from sandbox_scripts.helpers.Networking.base_save_restore import * from sandbox_scripts.QualiEnvironmentUtils.QualiUtils import QualiError from sandbox_scripts.QualiEnvironmentUtils.QualiUtils import rsc_run_result_struct +from cloudshell.api.cloudshell_api import InputNameValue class NetworkingSaveRestore(object): def __init__(self, sandbox): @@ -84,7 +85,6 @@ def load_config(self, config_stage, config_type, restore_method="Override", conf health_check_attempts, lock, use_Config_file_path_attr,in_teardown_mode)) for resource in root_resources] - pool.close() pool.join() for async_result in async_results: @@ -99,8 +99,28 @@ def load_config(self, config_stage, config_type, restore_method="Override", conf self.sandbox.report_info(res.resource_name + "\n" + res.message, write_to_output_window=True) if remove_temp_files: self._remove_temp_config_files() + + #Run Validate L2 Routes command on each IOS or NXOS device; pause for ports to come 'detected' + if not in_teardown_mode: + been_here = False + for resource in root_resources: + if resource.has_command('Validate_L2_Routes'): + if been_here is False: + been_here = True + self.sandbox.report_info('Pausing 60s before "Validate L2 Routes"', write_to_output_window=True) + time.sleep(90) + try: + command_inputs = [InputNameValue('Show_Not_Found_Only', 'y')] + self.sandbox.api_session.ExecuteCommand(self.sandbox.id, resource.name, 'Resource', + "Validate_L2_Routes", + commandInputs=command_inputs, + printOutput=True) + except Exception as ex: + self.sandbox.report_error("Validate L2 Routes failed to run for " + resource.name + + ' ex: ' + ex.message, + write_to_output_window=True) else: - self.sandbox.report_info("No networking resources found to process.") + self.sandbox.report_info("No networking resources found to process.", write_to_output_window=True) # ---------------------------------- # ---------------------------------- @@ -159,7 +179,7 @@ def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, config_path = '' with lock: if config_stage == 'Snapshots': - config_path = root_path + resource.name + '_' + resource.model + '.cfg' + config_path = root_path + resource.name + '_' + resource.model.replace(' ','_') + '.cfg' self.sandbox.report_info("Using " + str(config_path)) else: config_path = self._get_concrete_config_file_path(root_path, resource, config_stage, @@ -253,7 +273,7 @@ def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, message += err load_result.message = message - + #self.sandbox.report_info('power off control disabled in this vers', write_to_output_window=True) if is_power_ctrl_ok and in_teardown_mode and resource.model not in ignore_models: # Attempt PowerOFF. # If threshold is a Negative value, no power off. @@ -345,11 +365,11 @@ def _get_concrete_config_file_path(self, root_path, resource, config_stage, writ if config_set_pool_resource is not None: config_set_pool_manager = ConfigPoolManager(sandbox=self.sandbox, pool_resource=config_set_pool_resource) config_set_pool_data = config_set_pool_manager.pool_data - config_path = root_path + resource.alias + '_' + resource.model + '.cfg' + config_path = root_path + resource.alias + '_' + resource.model.replace(' ','_') + '.cfg' config_path = config_path.replace(' ', '_') # Look for a template config file tmp_template_config_file = tempfile.NamedTemporaryFile(delete=False) - tftp_template_config_path = root_path + resource.alias + '_' + resource.model + '.tm' + tftp_template_config_path = root_path + resource.alias + '_' + resource.model.replace(' ','_') + '.tm' tftp_template_config_path = tftp_template_config_path.replace(' ', '_') # try: # look for a concrete config file @@ -371,8 +391,9 @@ def _get_concrete_config_file_path(self, root_path, resource, config_stage, writ tmp_template_config_file_data = content_file.read() concrete_config_data = '' + subst_log = '' try: - concrete_config_data = config_file_mgr.create_concrete_config_from_template( + concrete_config_data, subst_log = config_file_mgr.create_concrete_config_from_template( tmp_template_config_file_data, config_set_pool_data, self.sandbox, resource) @@ -381,7 +402,15 @@ def _get_concrete_config_file_path(self, root_path, resource, config_stage, writ 'for resource {0}'.format(resource.name), log_message=qe.message, write_to_output_window=True) - + if resource.attribute_exist('Subst Log') and subst_log > '': + now = datetime.datetime.now() + subst_log = self.sandbox.id + ' @ ' + now.strftime("%Y-%m-%d %H:%M") + '\n' + subst_log + try: + resource.set_attribute_value('Subst_Log', subst_log) + except QualiError as qe: + self.sandbox.report_error('Could not post subst_log data for resource {0}'.format(resource.name), + log_message=qe.message, + write_to_output_window=True) tmp_concrete_config_file = tempfile.NamedTemporaryFile(delete=False) tf = file(tmp_concrete_config_file.name, 'wb+') tf.write(concrete_config_data) From d5f7dcb1aa44c908c435fb98dfc3a6e8a9c27c18 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Wed, 27 Sep 2017 15:16:17 -0700 Subject: [PATCH 37/50] Edits to fix tests to align with returning resource name, mostly --- .../tests/test_ConfigFileManager.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/sandbox_scripts/QualiEnvironmentUtils/tests/test_ConfigFileManager.py b/sandbox_scripts/QualiEnvironmentUtils/tests/test_ConfigFileManager.py index 0a80e39..d802d8e 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/tests/test_ConfigFileManager.py +++ b/sandbox_scripts/QualiEnvironmentUtils/tests/test_ConfigFileManager.py @@ -21,7 +21,13 @@ def test_values_only_from_pool_when_pool_doesnt_have_the_attribute(self, mock_ap config_file_mgr = ConfigFileManager() sandbox = None - resource = None + rd = Mock() + rd.Name = 'myresource' + rd.Address = '1.2.3.4' + rd.ChildResources = [] + rd.ResourceAttributes = [] + mock_api_session.return_value.GetResourceDetails = Mock(return_value=rd) + resource = ResourceBase(resource_name='myresource') tmp_template_config_file_data = """ {ConfigPool:Pool1} @@ -34,8 +40,8 @@ def test_values_only_from_pool_when_pool_doesnt_have_the_attribute(self, mock_ap the_exception = e.exception self.assertEqual(str(the_exception), - "CloudShell error at ConfigFileManager. " - "Error is: Failed to create a concrete config file from the template's data. " + "CloudShell error at ConfigFileManager. Error is: Failed to create concrete config " + "for myresource from template. " "Unexpected error: Could not find attribute {configpool:pool1} in the config pool") @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session') @@ -199,7 +205,7 @@ def test_attribute_from_other_resource_in_sandbox_not_found(self, mock_api_sessi the_exception = e.exception self.assertEqual(str(the_exception), "CloudShell error at ConfigFileManager. " - "Error is: Failed to create a concrete config file from the template's data. " + "Error is: Failed to create concrete config for myresource from template. " "Unexpected error: Could not find attribute 'Pool123' in resource 'myresource'") @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session') @@ -229,8 +235,8 @@ def test_attribute_from_other_resource_in_sandbox_resource_not_found(self, mock_ the_exception = e.exception self.assertEqual(str(the_exception), - "CloudShell error at ConfigFileManager. Error is: " - "Failed to create a concrete config file from the template's data. " + "CloudShell error at ConfigFileManager. Error is: Failed to create concrete config " + "for myresource from template. " "Unexpected error: Could not find a resource with alias OtherDevice2; " "likely missing from blueprint.") From 4f71f5a5d10ec3f3b7bc7515c660ffbd99defb1e Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Wed, 27 Sep 2017 15:17:19 -0700 Subject: [PATCH 38/50] Removed us of subst_log variable that was passed back from config file manager, creating some test havoc but keeping the variable for debug use (interactive in pycharm type use) only --- sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py b/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py index 502f14a..ab616d6 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py +++ b/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py @@ -22,7 +22,10 @@ def create_concrete_config_from_template(self, template_config_data, config_set_ :param SandboxBase sandbox: The sandbox to get other resources values from :param ResourceBase resource: The resource we want to create the config file for """ + # if resource is None: + # raise QualiError('') try: + concrete_config_data = template_config_data subst_log = "" # Replace {ConfigPool.PARAM} with PARAM's value from the pool @@ -89,7 +92,7 @@ def create_concrete_config_from_template(self, template_config_data, config_set_ if concrete_name <= ' ': raise Exception('Could not find a resource with alias ' + sb_alias + '; likely missing from blueprint.') - return concrete_config_data, subst_log + return concrete_config_data #, subst_log except Exception as ex: raise QualiError('ConfigFileManager', "Failed to create concrete config for " + resource.name + " from template. Unexpected error: " + ex.message) From d58c97844ad672065d484576b86039375a9ee9c4 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Wed, 27 Sep 2017 15:17:48 -0700 Subject: [PATCH 39/50] Removed subst_log usage --- .../helpers/Networking/NetworkingSaveNRestore.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py b/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py index 31d2c5f..5288350 100644 --- a/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py +++ b/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py @@ -391,9 +391,9 @@ def _get_concrete_config_file_path(self, root_path, resource, config_stage, writ tmp_template_config_file_data = content_file.read() concrete_config_data = '' - subst_log = '' + try: - concrete_config_data, subst_log = config_file_mgr.create_concrete_config_from_template( + concrete_config_data = config_file_mgr.create_concrete_config_from_template( tmp_template_config_file_data, config_set_pool_data, self.sandbox, resource) @@ -402,15 +402,7 @@ def _get_concrete_config_file_path(self, root_path, resource, config_stage, writ 'for resource {0}'.format(resource.name), log_message=qe.message, write_to_output_window=True) - if resource.attribute_exist('Subst Log') and subst_log > '': - now = datetime.datetime.now() - subst_log = self.sandbox.id + ' @ ' + now.strftime("%Y-%m-%d %H:%M") + '\n' + subst_log - try: - resource.set_attribute_value('Subst_Log', subst_log) - except QualiError as qe: - self.sandbox.report_error('Could not post subst_log data for resource {0}'.format(resource.name), - log_message=qe.message, - write_to_output_window=True) + tmp_concrete_config_file = tempfile.NamedTemporaryFile(delete=False) tf = file(tmp_concrete_config_file.name, 'wb+') tf.write(concrete_config_data) From 5c3d15a6b9657a3b4572247277ec387f85ef60e5 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 5 Oct 2017 10:30:47 -0700 Subject: [PATCH 40/50] per code revieiw removed repeated setting of ignore models --- sandbox_scripts/environment/teardown/teardown_resources.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/sandbox_scripts/environment/teardown/teardown_resources.py b/sandbox_scripts/environment/teardown/teardown_resources.py index cfd34ed..90bc257 100644 --- a/sandbox_scripts/environment/teardown/teardown_resources.py +++ b/sandbox_scripts/environment/teardown/teardown_resources.py @@ -24,9 +24,6 @@ def execute(self): sandbox.clear_all_resources_live_status(ignore_models) try: if saveNRestoreTool.get_storage_manager(): - ignore_models = ['Generic TFTP server', 'Config Set Pool', 'Generic FTP server', - 'netscout switch 3912', 'OnPATH Switch 3903', 'Ixia Traffic generator', - "SubNet-28", "SubNet-30", "GitLab", 'SSID_Pool'] saveNRestoreTool.load_config(config_stage='Base', config_type='Running', ignore_models=ignore_models, From 1580e38580dd94ef2f228205f4c06a2434a39b46 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 5 Oct 2017 10:34:52 -0700 Subject: [PATCH 41/50] commented out settings of subst_log. Left in - very valuable to isolate an issue with a tempate containing bad characters, like a tab. uncomment for debugging use. --- .../QualiEnvironmentUtils/ConfigFileManager.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py b/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py index ab616d6..cc0b872 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py +++ b/sandbox_scripts/QualiEnvironmentUtils/ConfigFileManager.py @@ -25,16 +25,15 @@ def create_concrete_config_from_template(self, template_config_data, config_set_ # if resource is None: # raise QualiError('') try: - concrete_config_data = template_config_data - subst_log = "" + #subst_log = resource.name + '\n' # This is for debug use - uncoment any statements using it if desired # Replace {ConfigPool.PARAM} with PARAM's value from the pool it = re.finditer(r'\{ConfigPool\:[^}]*\}', concrete_config_data, flags=re.IGNORECASE) for match in it: param = match.group() if param.lower() in config_set_pool_data: concrete_config_data = concrete_config_data.replace(param, config_set_pool_data[param.lower()]) - subst_log += param + ' = ' + config_set_pool_data[param.lower()] + '\n' + #subst_log += param + ' = ' + config_set_pool_data[param.lower()] + '\n' else: raise Exception('Could not find attribute ' + param.lower() + ' in the config pool') @@ -44,19 +43,19 @@ def create_concrete_config_from_template(self, template_config_data, config_set_ param = match.group() quali_note = 'Built from template: ' + strftime('%Y-%b-%d %H:%M:%S', gmtime()) concrete_config_data = concrete_config_data.replace(param, quali_note) - subst_log += param + ' = ' + quali_note + '\n' + #subst_log += param + ' = ' + quali_note + '\n' # Replace {Device.Self.Name} with the resource's name it = re.finditer(r'\{Device:Self:Name\}', concrete_config_data, flags=re.IGNORECASE) for match in it: param = match.group() concrete_config_data = concrete_config_data.replace(param, resource.name) - subst_log += param + ' = ' + resource.name + '\n' + #subst_log += param + ' = ' + resource.name + '\n' # Replace {Device.Self.Address} with the resource's management ip it = re.finditer(r'\{Device:Self:Address\}', concrete_config_data, flags=re.IGNORECASE) for match in it: param = match.group() concrete_config_data = concrete_config_data.replace(param, resource.address) - subst_log += param + ' = ' + resource.address + '\n' + #subst_log += param + ' = ' + resource.address + '\n' # Replace {Device.Self.ATTRIBUTE_NAME} with the resource's attribute value # TODO: Need to decode password attributes: Password, Enable Password, and SNMP Read Community it = re.finditer(r'\{Device:Self\:[^}]*\}', concrete_config_data, flags=re.IGNORECASE) @@ -67,7 +66,7 @@ def create_concrete_config_from_template(self, template_config_data, config_set_ param_val = resource.get_attribute(att_name) # param_val = resource.get_attribute(param) concrete_config_data = concrete_config_data.replace(param, param_val) - subst_log += param + ' = ' + param_val + '\n' + #subst_log += param + ' = ' + param_val + '\n' # Replacement of params from types: {Device:ALIAS:Attribute_name} WHERE ALIAS is at any structure level it = re.finditer(r'\{Device:[^}]*\}', concrete_config_data, flags=re.IGNORECASE) the_resources = None @@ -87,7 +86,7 @@ def create_concrete_config_from_template(self, template_config_data, config_set_ raise Exception("Could not find attribute '{0}' in resource '{1}'".format(alias_attribname, resource.name)) concrete_config_data = concrete_config_data.replace(param, param_val) - subst_log += param + ' = ' + param_val + '\n' + #subst_log += param + ' = ' + param_val + '\n' break if concrete_name <= ' ': raise Exception('Could not find a resource with alias ' + sb_alias + '; likely missing from blueprint.') From 28e3e6c06e6a43746b148be11631da6e2b807761 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 5 Oct 2017 10:35:44 -0700 Subject: [PATCH 42/50] removed unnecessary passing in of resource name per code review --- sandbox_scripts/QualiEnvironmentUtils/Resource.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sandbox_scripts/QualiEnvironmentUtils/Resource.py b/sandbox_scripts/QualiEnvironmentUtils/Resource.py index fe6d04b..fd5f7b9 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/Resource.py +++ b/sandbox_scripts/QualiEnvironmentUtils/Resource.py @@ -116,9 +116,9 @@ def set_attribute_value(self, attribute_name, attribute_value): # ----------------------------------------- # ----------------------------------------- - def get_upcoming(self, dev_name, period_start, period_end): + def get_upcoming(self, period_start, period_end): try: - upcoming = self.api_session.GetResourceAvailabilityInTimeRange(resourcesNames=([dev_name]), + upcoming = self.api_session.GetResourceAvailabilityInTimeRange(resourcesNames=([self.name]), startTime=period_start, endTime=period_end, showAllDomains=False) From c9906e5e8f77d6117fdf086e9e10629b9a7cef03 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 5 Oct 2017 10:36:44 -0700 Subject: [PATCH 43/50] Added a TODO in case we get an api to change description. Per code review --- sandbox_scripts/QualiEnvironmentUtils/Sandbox.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py b/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py index 8a404f0..d3a2073 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py +++ b/sandbox_scripts/QualiEnvironmentUtils/Sandbox.py @@ -422,6 +422,7 @@ def setcategorysnapshots(self, snapshot_name): # ----------------------------------------- # ----------------------------------------- + # TODO When we can change desc by api.... def update_description(self, description): # No api exists yet for this feature return From a8f5bbcc1d7c70b81f6beff51b9166895ff5caa2 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 5 Oct 2017 11:57:01 -0700 Subject: [PATCH 44/50] per code review --- .../QualiEnvironmentUtils/Resource.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/sandbox_scripts/QualiEnvironmentUtils/Resource.py b/sandbox_scripts/QualiEnvironmentUtils/Resource.py index fd5f7b9..9a3980b 100644 --- a/sandbox_scripts/QualiEnvironmentUtils/Resource.py +++ b/sandbox_scripts/QualiEnvironmentUtils/Resource.py @@ -9,6 +9,10 @@ import json from time import sleep + +POWER_ON_WL = ["power_on", "Power On", "Power ON", "PowerOn"] # whitelist for power on commands +POWER_OFF_WL = ["power_off", "Power Off", "Power OFF", "PowerOff"] # whitelist for power off commands + class ResourceBase(object): def __init__(self, resource_name, resource_alias=''): if resource_name != "": @@ -31,8 +35,6 @@ def __init__(self, resource_name, resource_alias=''): self.model = self.model.replace('Cisco 5500 Series Wireless LAN Controller','WLC') self.alias = resource_alias - self.power_on_wl = ["power_on", "Power On", "Power ON", "PowerOn"] # whitelist for power on commands - self.power_off_wl = ["power_off", "Power Off", "Power OFF", "PowerOff"] # whitelist for power off commands # ----------------------------------------- # ----------------------------------------- @@ -49,7 +51,7 @@ def has_command(self, command_name): # ----------------------------------------- def has_power_on(self): for command in self.commands: - if command.Name in self.power_on_wl: + if command.Name in POWER_ON_WL: return command.Name return "" @@ -57,7 +59,7 @@ def has_power_on(self): # ----------------------------------------- def has_power_off(self): for command in self.commands: - if command.Name in self.power_off_wl: + if command.Name in POWER_OFF_WL: return command.Name return "" @@ -65,7 +67,7 @@ def has_power_off(self): # ----------------------------------------- def has_connected_power_on(self): for command in self.connected_commands: - if command.Name in self.power_on_wl: + if command.Name in POWER_ON_WL: return command.Name return "" @@ -73,7 +75,7 @@ def has_connected_power_on(self): # ----------------------------------------- def has_connected_power_off(self): for command in self.connected_commands: - if command.Name in self.power_off_wl: + if command.Name in POWER_OFF_WL: return command.Name return "" @@ -322,7 +324,7 @@ def orchestration_save(self, reservation_id, config_path, config_type): if 'orchestration_save' == command.Name: tag = command.Tag config_name = self.execute_connected_command(reservation_id, 'orchestration_save', tag, - commandInputs=['shallow',''], printOutput=False) + commandInputs=[InputNameValue('shallow','')], printOutput=False) return config_name.Output else: From 551c852c7b23813daf406a0a8f97941c99cd27f2 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 5 Oct 2017 11:57:23 -0700 Subject: [PATCH 45/50] per code review --- .../Networking/NetworkingSaveNRestore.py | 150 +++++++++--------- 1 file changed, 74 insertions(+), 76 deletions(-) diff --git a/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py b/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py index 5288350..da1f699 100644 --- a/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py +++ b/sandbox_scripts/helpers/Networking/NetworkingSaveNRestore.py @@ -107,10 +107,11 @@ def load_config(self, config_stage, config_type, restore_method="Override", conf if resource.has_command('Validate_L2_Routes'): if been_here is False: been_here = True - self.sandbox.report_info('Pausing 60s before "Validate L2 Routes"', write_to_output_window=True) + self.sandbox.report_info('Pausing 90s before "Validate L2 Routes"', write_to_output_window=True) time.sleep(90) try: command_inputs = [InputNameValue('Show_Not_Found_Only', 'y')] + # TODO See why we can't change this to use resource.execute command self.sandbox.api_session.ExecuteCommand(self.sandbox.id, resource.name, 'Resource', "Validate_L2_Routes", commandInputs=command_inputs, @@ -142,6 +143,7 @@ def _remove_temp_config_files(self): # ---------------------------------- def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, config_stage, health_check_attempts, lock, use_Config_file_path_attr, in_teardown_mode): + # TODO resolve different approach - message var vs real-time sandbox op window messages. message = "" # run_status = True saved_artifact_info = None @@ -170,6 +172,7 @@ def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, load_config_to_device = self._is_load_config_to_device(resource, ignore_models=ignore_models) # ------------------------------------------------------ #if load_config_to_device and '3850-6' in resource.name: + # TODO change form save/restore to orchestration save and restore - high priority. if load_config_to_device: self.sandbox.report_info(resource.name + " - starting health check", write_to_output_window=True) health_check_result = resource.health_check(self.sandbox.id, health_check_attempts) @@ -178,13 +181,9 @@ def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, try: config_path = '' with lock: - if config_stage == 'Snapshots': - config_path = root_path + resource.name + '_' + resource.model.replace(' ','_') + '.cfg' - self.sandbox.report_info("Using " + str(config_path)) - else: - config_path = self._get_concrete_config_file_path(root_path, resource, config_stage, + config_path = self._get_concrete_config_file_path(root_path, resource, config_stage, write_to_output=False) - self.sandbox.report_info("Config path for " + resource.alias + ": " + str(config_path)) + self.sandbox.report_info("Config path for " + resource.alias + ": " + str(config_path)) if use_Config_file_path_attr: resource.set_attribute_value('Config file path', config_path) # TODO - Snapshots currently only restore configuration. We need to restore firmware as well @@ -213,8 +212,8 @@ def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, if image_key: #self.sandbox.report_info("Using image key " + str(image_key) + " for " + resource.name) dict_img_version = images_path_dict[image_key].version - if version < ' ': - self.sandbox.report_error('Firmware spec file is missing version element for ' + resource.name) + if dict_img_version == '': + self.sandbox.report_error('Failed to find target version for ' + resource.name) else: # Getting here means no firmware specified in FirmwareData.csv message += "\n" + resource.name + ": NO firmware version specified in FirmwareData.csv" @@ -274,6 +273,7 @@ def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, load_result.message = message #self.sandbox.report_info('power off control disabled in this vers', write_to_output_window=True) + # TODO more to resource class if is_power_ctrl_ok and in_teardown_mode and resource.model not in ignore_models: # Attempt PowerOFF. # If threshold is a Negative value, no power off. @@ -289,7 +289,7 @@ def _run_asynch_load(self, resource, images_path_dict, root_path, ignore_models, if int(threshold) > 4: period_start = (datetime.datetime.now() + datetime.timedelta(minutes=2)).isoformat() period_end = (datetime.datetime.now() + datetime.timedelta(minutes=int(threshold))).isoformat() - upcoming = resource.get_upcoming(resource.name, period_start=period_start, period_end=period_end) + upcoming = resource.get_upcoming(period_start=period_start, period_end=period_end) for element in upcoming.Resources: if element.Name == resource.name: if element.Reservations.__len__() > 0: @@ -357,69 +357,72 @@ def _get_concrete_config_file_path(self, root_path, resource, config_stage, writ :rtype: str """ - config_file_mgr = ConfigFileManager() - # TODO - set the pool dictionary only once during the init of the class - config_set_pool_data = dict() - # If there is a pool resource, get the pool data - config_set_pool_resource = self.sandbox.get_config_set_pool_resource() - if config_set_pool_resource is not None: - config_set_pool_manager = ConfigPoolManager(sandbox=self.sandbox, pool_resource=config_set_pool_resource) - config_set_pool_data = config_set_pool_manager.pool_data - config_path = root_path + resource.alias + '_' + resource.model.replace(' ','_') + '.cfg' - config_path = config_path.replace(' ', '_') - # Look for a template config file - tmp_template_config_file = tempfile.NamedTemporaryFile(delete=False) - tftp_template_config_path = root_path + resource.alias + '_' + resource.model.replace(' ','_') + '.tm' - tftp_template_config_path = tftp_template_config_path.replace(' ', '_') - # try: - # look for a concrete config file - try: - self.storage_mgr.download(config_path, tmp_template_config_file.name) - tmp_template_config_file.close() - os.unlink(tmp_template_config_file.name) - # if no concrete file, delete the look for a template file - except: + if config_stage == 'Snapshots': + config_path = root_path + resource.name + '_' + resource.model.replace(' ', '_') + '.cfg' + else: + config_file_mgr = ConfigFileManager() + # TODO - set the pool dictionary only once during the init of the class + config_set_pool_data = dict() + # If there is a pool resource, get the pool data + config_set_pool_resource = self.sandbox.get_config_set_pool_resource() + if config_set_pool_resource is not None: + config_set_pool_manager = ConfigPoolManager(sandbox=self.sandbox, pool_resource=config_set_pool_resource) + config_set_pool_data = config_set_pool_manager.pool_data + config_path = root_path + resource.alias + '_' + resource.model.replace(' ','_') + '.cfg' + config_path = config_path.replace(' ', '_') + # Look for a template config file + tmp_template_config_file = tempfile.NamedTemporaryFile(delete=False) + tftp_template_config_path = root_path + resource.alias + '_' + resource.model + '.tm' + tftp_template_config_path = tftp_template_config_path.replace(' ', '_') + # try: + # look for a concrete config file try: - self.storage_mgr.download(tftp_template_config_path, tmp_template_config_file.name) - except Exception as ex: - print ex.message - # look for a generic config file for the model - tftp_template_config_path = root_path + resource.model + '.tm' - tftp_template_config_path = tftp_template_config_path.replace(' ', '_') - self.storage_mgr.download(tftp_template_config_path, tmp_template_config_file.name) - with open(tmp_template_config_file.name, 'r') as content_file: - tmp_template_config_file_data = content_file.read() + self.storage_mgr.download(config_path, tmp_template_config_file.name) + tmp_template_config_file.close() + os.unlink(tmp_template_config_file.name) + # if no concrete file, delete the look for a template file + except: + try: + self.storage_mgr.download(tftp_template_config_path, tmp_template_config_file.name) + except Exception as ex: + self.sandbox.report_info(ex.message) + # look for a generic config file for the model + tftp_template_config_path = root_path + resource.model + '.tm' + tftp_template_config_path = tftp_template_config_path.replace(' ', '_') + self.storage_mgr.download(tftp_template_config_path, tmp_template_config_file.name) + with open(tmp_template_config_file.name, 'r') as content_file: + tmp_template_config_file_data = content_file.read() - concrete_config_data = '' + concrete_config_data = '' - try: - concrete_config_data = config_file_mgr.create_concrete_config_from_template( - tmp_template_config_file_data, - config_set_pool_data, - self.sandbox, resource) - except QualiError as qe: - self.sandbox.report_error(error_message='Could not create a concrete config file ' - 'for resource {0}'.format(resource.name), - log_message=qe.message, - write_to_output_window=True) - - tmp_concrete_config_file = tempfile.NamedTemporaryFile(delete=False) - tf = file(tmp_concrete_config_file.name, 'wb+') - tf.write(concrete_config_data) - tf.flush() - tf.close() - tmp_template_config_file.close() - os.unlink(tmp_template_config_file.name) - short_Reservation_id = self.sandbox.id[len(self.sandbox.id) - 4:len(self.sandbox.id)] - concrete_file_path = root_path + 'temp/' + short_Reservation_id + '_' + resource.alias + \ - '_' + resource.model + '.cfg' - concrete_file_path = concrete_file_path.replace(' ', '_') - # TODO - clean the temp dir on the tftp server - self.storage_mgr.upload(concrete_file_path, tmp_concrete_config_file.name) - tmp_concrete_config_file.close() - os.unlink(tmp_concrete_config_file.name) - # Set the path to the new concrete file - config_path = concrete_file_path + try: + concrete_config_data = config_file_mgr.create_concrete_config_from_template( + tmp_template_config_file_data, + config_set_pool_data, + self.sandbox, resource) + except QualiError as qe: + self.sandbox.report_error(error_message='Could not create a concrete config file ' + 'for resource {0}'.format(resource.name), + log_message=qe.message, + write_to_output_window=True) + + tmp_concrete_config_file = tempfile.NamedTemporaryFile(delete=False) + tf = file(tmp_concrete_config_file.name, 'wb+') + tf.write(concrete_config_data) + tf.flush() + tf.close() + tmp_template_config_file.close() + os.unlink(tmp_template_config_file.name) + short_Reservation_id = self.sandbox.id[len(self.sandbox.id) - 4:len(self.sandbox.id)] + concrete_file_path = root_path + 'temp/' + short_Reservation_id + '_' + resource.alias + \ + '_' + resource.model + '.cfg' + concrete_file_path = concrete_file_path.replace(' ', '_') + # TODO - clean the temp dir on the tftp server + self.storage_mgr.upload(concrete_file_path, tmp_concrete_config_file.name) + tmp_concrete_config_file.close() + os.unlink(tmp_concrete_config_file.name) + # Set the path to the new concrete file + config_path = concrete_file_path return config_path @@ -442,7 +445,7 @@ def save_config(self, snapshot_name, config_type, ignore_models=None, write_to_o write_to_output_window=True) else: self.storage_mgr.create_dir(env_dir, write_to_output=True) - self.sandbox.report_info("Configs saving to\n" + env_dir, write_to_output_window=True) + # self.sandbox.report_info("Configs saving to\n" + env_dir, write_to_output_window=True) except QualiError as e: self.sandbox.report_error("Save snapshot failed attempting to make dir. " + str(e), write_to_output_window=write_to_output, raise_error=True) @@ -526,11 +529,6 @@ def _is_load_config_to_device(self, resource, ignore_models=None): if resource.model.lower() == ignore_model.lower(): return False - apps = self.sandbox.get_Apps_resources() - for app in apps: - if app.Name == resource.name: - return False - return True def _is_power_ctrl_ok(self, resource, ignore_models=None): From b05cec3d999624660d1d321081bdfb9ff9f601e0 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 5 Oct 2017 18:51:13 -0700 Subject: [PATCH 46/50] fixed tests --- .../setup/tests/test_setup_resources.py | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/sandbox_scripts/environment/setup/tests/test_setup_resources.py b/sandbox_scripts/environment/setup/tests/test_setup_resources.py index 000b009..99ec4c4 100644 --- a/sandbox_scripts/environment/setup/tests/test_setup_resources.py +++ b/sandbox_scripts/environment/setup/tests/test_setup_resources.py @@ -39,12 +39,13 @@ def test_flow_ok_with_snapshots(self, mock_save, mock_sandboxbase, mock_api_sess mock_save.return_value.is_snapshot.return_value = True self.setup_script.execute() - 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', 'Subnet-28', 'Subnet-30', 'GitLab']) + mock_sandboxbase.return_value.clear_all_resources_live_status.assert_called_with(['Generic TFTP server', 'Config Set Pool', 'Generic FTP server', 'netscout switch 3912', 'Subnet-28', 'Subnet-30', 'GitLab', 'SSID_Pool']) + 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', 'Subnet-28', 'Subnet-30', 'GitLab', 'SSID_Pool']) mock_sandboxbase.return_value.power_on_vms.assert_called_with() mock_sandboxbase.return_value.activate_all_routes_and_connectors.assert_called_with() report_info_calls = [call('Beginning load configuration for resources'), - call('Sandbox setup finished successfully')] + call('Clearing status indicators ', write_to_output_window=True), + call('Sandbox setup finished.', write_to_output_window=True)] mock_sandboxbase.return_value.report_info.assert_has_calls(report_info_calls) @@ -55,12 +56,15 @@ def test_flow_ok_with_gold(self, mock_save, mock_sandboxbase, mock_api_session): mock_save.return_value.is_snapshot.return_value = False mock_sandboxbase.return_value.get_storage_server_resource.return_value = True self.setup_script.execute() - 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', 'Subnet-28', 'Subnet-30', 'GitLab']) + mock_sandboxbase.return_value.clear_all_resources_live_status.assert_called_with( + ['Generic TFTP server', 'Config Set Pool', 'Generic FTP server', 'netscout switch 3912', 'Subnet-28', + 'Subnet-30', 'GitLab', 'SSID_Pool']) + 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', 'Subnet-28', 'Subnet-30', 'GitLab', 'SSID_Pool']) mock_sandboxbase.return_value.power_on_vms.assert_called_with() mock_sandboxbase.return_value.activate_all_routes_and_connectors.assert_called_with() report_info_calls = [call('Beginning load configuration for resources'), - call('Sandbox setup finished successfully')] + call('Clearing status indicators ', write_to_output_window=True), + call('Sandbox setup finished.', write_to_output_window=True)] mock_sandboxbase.return_value.report_info.assert_has_calls(report_info_calls) @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session') @@ -69,10 +73,13 @@ def test_flow_ok_with_gold(self, mock_save, mock_sandboxbase, mock_api_session): def test_flow_ok_with_no_storage_device(self, mock_save, mock_sandboxbase, mock_api_session): mock_sandboxbase.return_value.get_storage_server_resource.return_value = False self.setup_script.execute() - mock_sandboxbase.return_value.clear_all_resources_live_status.assert_called_with() + mock_sandboxbase.return_value.clear_all_resources_live_status.assert_called_with( + ['Generic TFTP server', 'Config Set Pool', 'Generic FTP server', 'netscout switch 3912', 'Subnet-28', + 'Subnet-30', 'GitLab', 'SSID_Pool']) report_info_calls = [call('Beginning load configuration for resources'), + call('Clearing status indicators ', write_to_output_window=True), call('Skipping load configuration. No storage resource associated with the blueprint ', write_to_output_window=True), - call('Sandbox setup finished successfully')] + call('Sandbox setup finished.', write_to_output_window=True)] mock_sandboxbase.return_value.report_info.assert_has_calls(report_info_calls) @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session') @@ -84,6 +91,7 @@ def test_qualierror_exception(self, mock_save, mock_sandboxbase, mock_api_sessio self.setup_script.execute() report_info_calls = [call('Beginning load configuration for resources'), + call('Clearing status indicators ', write_to_output_window=True), call('Skipping load configuration. No storage resource associated with the blueprint ', write_to_output_window=True)] mock_sandboxbase.return_value.report_info.assert_has_calls(report_info_calls) From c35525a0298a690a5cd7e7879054745f0107dea2 Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 5 Oct 2017 19:03:12 -0700 Subject: [PATCH 47/50] fixed tests. --- sandbox_scripts/environment/setup/setup_VM.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sandbox_scripts/environment/setup/setup_VM.py b/sandbox_scripts/environment/setup/setup_VM.py index e81b5a9..d739202 100644 --- a/sandbox_scripts/environment/setup/setup_VM.py +++ b/sandbox_scripts/environment/setup/setup_VM.py @@ -16,8 +16,8 @@ def __init__(self): log_group=self.reservation_id, log_category='Setup') - # --------------------------- - # --------------------------- + # ---------------------------- + # ---------------------------- def execute(self): self.sandbox = SandboxBase(self.reservation_id, self.logger) #TODO: don't use networking save and restore to figure if it's a snapshot setup From 311ff2534e9446b19a4e39d3c33f8330e82f0f4f Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 5 Oct 2017 19:10:36 -0700 Subject: [PATCH 48/50] fixed tests - all pass --- sandbox_scripts/environment/setup/setup_script.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sandbox_scripts/environment/setup/setup_script.py b/sandbox_scripts/environment/setup/setup_script.py index 0ed694d..1595f09 100644 --- a/sandbox_scripts/environment/setup/setup_script.py +++ b/sandbox_scripts/environment/setup/setup_script.py @@ -57,7 +57,7 @@ def execute(self): self.logger.info("Setup for reservation {0} completed".format(self.reservation_id)) #api.WriteMessageToReservationOutput(reservationId=self.reservation_id, - # message='Sandbox setup finished successfully') + # message='Sandbox setup finished successfully') def _prepare_connectivity(self, api, reservation_id): """ From 7d33b7b14d2565f4c42a6003e8eb5005d1b8b2cb Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 5 Oct 2017 19:20:16 -0700 Subject: [PATCH 49/50] repaired --- .../{setup => setupS}/tests/test_setup_VM.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) rename sandbox_scripts/environment/{setup => setupS}/tests/test_setup_VM.py (90%) diff --git a/sandbox_scripts/environment/setup/tests/test_setup_VM.py b/sandbox_scripts/environment/setupS/tests/test_setup_VM.py similarity index 90% rename from sandbox_scripts/environment/setup/tests/test_setup_VM.py rename to sandbox_scripts/environment/setupS/tests/test_setup_VM.py index c0b236b..a4bb704 100644 --- a/sandbox_scripts/environment/setup/tests/test_setup_VM.py +++ b/sandbox_scripts/environment/setupS/tests/test_setup_VM.py @@ -1,6 +1,6 @@ import unittest from mock import patch, Mock, call -from sandbox_scripts.environment.setup.setup_VM import EnvironmentSetupVM +from sandbox_scripts.environment.setupS.setup_VM import EnvironmentSetupVM from sandbox_scripts.QualiEnvironmentUtils.QualiUtils import QualiError from cloudshell.api.common_cloudshell_api import CloudShellAPIError from cloudshell.api.cloudshell_api import ResourceInfoVmDetails @@ -25,7 +25,7 @@ class SetupVMTests(unittest.TestCase): - @patch('sandbox_scripts.environment.setup.setup_VM.get_qs_logger') + @patch('sandbox_scripts.environment.setupS.setup_VM.get_qs_logger') def setUp(self, mock_logger): os.environ['reservationContext'] = resContext os.environ['qualiConnectivityContext'] = conContext @@ -35,8 +35,8 @@ def tearDown(self): pass @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session') - @patch('sandbox_scripts.environment.setup.setup_VM.SandboxBase') - @patch('sandbox_scripts.environment.setup.setup_VM.SaveRestoreManager') + @patch('sandbox_scripts.environment.setupS.setup_VM.SandboxBase') + @patch('sandbox_scripts.environment.setupS.setup_VM.SaveRestoreManager') def test_setup_vm_with_no_resources(self, mock_save, mock_sandboxbase, mock_api_session): self.setup_script.execute() @@ -46,8 +46,8 @@ def test_setup_vm_with_no_resources(self, mock_save, mock_sandboxbase, mock_api_ mock_sandboxbase.return_value.report_info.assert_has_calls(report_info_calls) @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session') - @patch('sandbox_scripts.environment.setup.setup_VM.SandboxBase') - @patch('sandbox_scripts.environment.setup.setup_VM.SaveRestoreManager') + @patch('sandbox_scripts.environment.setupS.setup_VM.SandboxBase') + @patch('sandbox_scripts.environment.setupS.setup_VM.SaveRestoreManager') def test_setup_vm_with_regular_resource(self, mock_save, mock_sandboxbase, mock_api_session): rdi = Mock() @@ -82,8 +82,8 @@ def resource_details_mock_side_effect(name): @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session') - @patch('sandbox_scripts.environment.setup.setup_VM.SandboxBase') - @patch('sandbox_scripts.environment.setup.setup_VM.SaveRestoreManager') + @patch('sandbox_scripts.environment.setupS.setup_VM.SandboxBase') + @patch('sandbox_scripts.environment.setupS.setup_VM.SaveRestoreManager') def test_setup_vm_with_static_app_resource_power_on_no_wait_for_ip(self, mock_save, mock_sandboxbase, mock_api_session): rdi = Mock() resource1 = Mock() @@ -131,8 +131,8 @@ def resource_details_mock_side_effect(name): @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session') - @patch('sandbox_scripts.environment.setup.setup_VM.SandboxBase') - @patch('sandbox_scripts.environment.setup.setup_VM.SaveRestoreManager') + @patch('sandbox_scripts.environment.setupS.setup_VM.SandboxBase') + @patch('sandbox_scripts.environment.setupS.setup_VM.SaveRestoreManager') def test_setup_vm_with_deployed_app_resource(self, mock_save, mock_sandboxbase, mock_api_session): rdi = Mock() resource1 = Mock() From 1f2dfa1afdfa57d851f0632f3c3244c08130b25a Mon Sep 17 00:00:00 2001 From: jimbr70 Date: Thu, 5 Oct 2017 19:21:52 -0700 Subject: [PATCH 50/50] fixed tests. --- .../{setupS => setup}/tests/test_setup_VM.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) rename sandbox_scripts/environment/{setupS => setup}/tests/test_setup_VM.py (90%) diff --git a/sandbox_scripts/environment/setupS/tests/test_setup_VM.py b/sandbox_scripts/environment/setup/tests/test_setup_VM.py similarity index 90% rename from sandbox_scripts/environment/setupS/tests/test_setup_VM.py rename to sandbox_scripts/environment/setup/tests/test_setup_VM.py index a4bb704..c0b236b 100644 --- a/sandbox_scripts/environment/setupS/tests/test_setup_VM.py +++ b/sandbox_scripts/environment/setup/tests/test_setup_VM.py @@ -1,6 +1,6 @@ import unittest from mock import patch, Mock, call -from sandbox_scripts.environment.setupS.setup_VM import EnvironmentSetupVM +from sandbox_scripts.environment.setup.setup_VM import EnvironmentSetupVM from sandbox_scripts.QualiEnvironmentUtils.QualiUtils import QualiError from cloudshell.api.common_cloudshell_api import CloudShellAPIError from cloudshell.api.cloudshell_api import ResourceInfoVmDetails @@ -25,7 +25,7 @@ class SetupVMTests(unittest.TestCase): - @patch('sandbox_scripts.environment.setupS.setup_VM.get_qs_logger') + @patch('sandbox_scripts.environment.setup.setup_VM.get_qs_logger') def setUp(self, mock_logger): os.environ['reservationContext'] = resContext os.environ['qualiConnectivityContext'] = conContext @@ -35,8 +35,8 @@ def tearDown(self): pass @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session') - @patch('sandbox_scripts.environment.setupS.setup_VM.SandboxBase') - @patch('sandbox_scripts.environment.setupS.setup_VM.SaveRestoreManager') + @patch('sandbox_scripts.environment.setup.setup_VM.SandboxBase') + @patch('sandbox_scripts.environment.setup.setup_VM.SaveRestoreManager') def test_setup_vm_with_no_resources(self, mock_save, mock_sandboxbase, mock_api_session): self.setup_script.execute() @@ -46,8 +46,8 @@ def test_setup_vm_with_no_resources(self, mock_save, mock_sandboxbase, mock_api_ mock_sandboxbase.return_value.report_info.assert_has_calls(report_info_calls) @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session') - @patch('sandbox_scripts.environment.setupS.setup_VM.SandboxBase') - @patch('sandbox_scripts.environment.setupS.setup_VM.SaveRestoreManager') + @patch('sandbox_scripts.environment.setup.setup_VM.SandboxBase') + @patch('sandbox_scripts.environment.setup.setup_VM.SaveRestoreManager') def test_setup_vm_with_regular_resource(self, mock_save, mock_sandboxbase, mock_api_session): rdi = Mock() @@ -82,8 +82,8 @@ def resource_details_mock_side_effect(name): @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session') - @patch('sandbox_scripts.environment.setupS.setup_VM.SandboxBase') - @patch('sandbox_scripts.environment.setupS.setup_VM.SaveRestoreManager') + @patch('sandbox_scripts.environment.setup.setup_VM.SandboxBase') + @patch('sandbox_scripts.environment.setup.setup_VM.SaveRestoreManager') def test_setup_vm_with_static_app_resource_power_on_no_wait_for_ip(self, mock_save, mock_sandboxbase, mock_api_session): rdi = Mock() resource1 = Mock() @@ -131,8 +131,8 @@ def resource_details_mock_side_effect(name): @patch('cloudshell.helpers.scripts.cloudshell_scripts_helpers.get_api_session') - @patch('sandbox_scripts.environment.setupS.setup_VM.SandboxBase') - @patch('sandbox_scripts.environment.setupS.setup_VM.SaveRestoreManager') + @patch('sandbox_scripts.environment.setup.setup_VM.SandboxBase') + @patch('sandbox_scripts.environment.setup.setup_VM.SaveRestoreManager') def test_setup_vm_with_deployed_app_resource(self, mock_save, mock_sandboxbase, mock_api_session): rdi = Mock() resource1 = Mock()