Skip to content

Commit

Permalink
Merge pull request #7 from QualiSystems/dev
Browse files Browse the repository at this point in the history
Release 1.0.5, fixes #6
  • Loading branch information
Costya-Y committed Mar 15, 2020
2 parents 83aaf3e + 09b5e0b commit df7360f
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 375 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -4,3 +4,5 @@ build
*.pyc
glimmerglass/test.py
glimmerglass/test.py
/glimmerglass/test*.py
/Logs
2 changes: 1 addition & 1 deletion configuration/configuration.json
Expand Up @@ -5,7 +5,7 @@
"connection_type": "tcp",
"connection_port": 10034,
"device_login_prompt": "<",
"device_prompt": ";\\s+<",
"device_prompt": ";\\s*<",
"server_timeout": 10,
"runtime_configuration": "glimmerglass_runtime_configuration.json"
},
Expand Down
54 changes: 18 additions & 36 deletions glimmerglass/glimmerglass_driver_handler.py
Expand Up @@ -4,9 +4,9 @@
import re

from common.driver_handler_base import DriverHandlerBase
from common.helper.system_helper import get_file_folder
from common.resource_info import ResourceInfo
from common.configuration_parser import ConfigurationParser
from glimmerglass.tcp_session import GGTCPSession


class GlimmerglassDriverHandler(DriverHandlerBase):
Expand All @@ -24,6 +24,8 @@ def __init__(self):
self._port_logical_mode = ConfigurationParser.get("driver_variable", "port_mode")
self._custom_port_pairing = ConfigurationParser.get("driver_variable", "custom_port_pairing") or dict()
self._login_prompt = ConfigurationParser.get("common_variable", "device_login_prompt")
self._prompt = ConfigurationParser.get("common_variable", "device_prompt")
self._session = GGTCPSession()

def _incr_ctag(self):
self._ctag += 1
Expand All @@ -36,7 +38,7 @@ def login(self, address, username, password, command_logger=None):
address_data = address.split(":")
ip = address_data[0]
port = int(address_data[1])
if self._service_mode.lower() == "tl1":
if self._service_mode.lower() == u"tl1":
command = 'ACT-USER::{0}:{1}::{2};'.format(username, self._ctag, password)
command_result = self._session.connect(host=ip, username=username, password=password, command=command,
re_string=self._login_prompt, port=port)
Expand All @@ -59,9 +61,9 @@ def login(self, address, username, password, command_logger=None):
def _get_device_data(self):
device_data = dict()

if self._service_mode.lower() == "scpi":
if self._service_mode.lower() == u"scpi":
pass
elif self._service_mode.lower() == "tl1":
elif self._service_mode.lower() == u"tl1":
command = "rtrv-system-info:::{0};".format(self._incr_ctag())
device_data["system_info"] = self._session.send_command(command, re_string=self._prompt)

Expand All @@ -87,7 +89,6 @@ def _get_device_data(self):
return device_data

def get_resource_description(self, address, command_logger=None):
self._session.send_command("", re_string=self._login_prompt)
device_data = self._get_device_data()

self._resource_info = ResourceInfo()
Expand Down Expand Up @@ -229,15 +230,14 @@ def get_resource_description(self, address, command_logger=None):
return self._resource_info.convert_to_xml()

def map_uni(self, src_port, dst_port, command_logger=None):
if self._service_mode.lower() == "tl1":
self._session.send_command("", re_string=self._login_prompt)
src_in_port = min(int(src_port[1]), int(dst_port[1]))

dst_out_port = max(int(src_port[1]), int(dst_port[1]))

if self._service_mode.lower() == u"tl1":
if self._port_logical_mode.lower() == "logical":
src_in_port = str(10000 + int(src_in_port.split('-')[0]))
dst_out_port = str(20000 + int(dst_out_port.split('-')[1]))
src_in_port = str(10000 + int(src_port[1].split('-')[0]))
dst_out_port = str(20000 + int(dst_port[1].split('-')[1]))
else:
src_in_port = min(int(src_port[1]), int(dst_port[1]))

dst_out_port = max(int(src_port[1]), int(dst_port[1]))

command = "ent-crs-fiber::{0},{1}:{2};".format(src_in_port, dst_out_port, self._incr_ctag())
command_result = self._session.send_command(command, re_string=self._prompt)
Expand All @@ -247,8 +247,7 @@ def map_uni(self, src_port, dst_port, command_logger=None):
"Selected '{}' connection type is not supported".format(self._service_mode))

def map_bidi(self, src_port, dst_port, command_logger=None):
if self._service_mode.lower() == "tl1":
self._session.send_command("", re_string=self._login_prompt)
if self._service_mode.lower() == u"tl1":
if self._port_logical_mode.lower() == "logical":
source_port = str(src_port[1]).split('-')
destination_port = str(dst_port[1]).split('-')
Expand All @@ -263,11 +262,13 @@ def map_bidi(self, src_port, dst_port, command_logger=None):
command_logger.info(command_result)
else:
raise Exception(self.__class__.__name__,
"Selected '{}' connection type is not supported".format(self._service_mode))
"Bidirectional mapping supported only in logical port mode".format(self._service_mode))
else:
raise Exception(self.__class__.__name__,
"Selected '{}' connection type is not supported".format(self._service_mode))

def map_clear_to(self, src_port, dst_port, command_logger=None):
if self._service_mode.lower() == "tl1":
self._session.send_command("", re_string=self._login_prompt)
src_in_port = src_port[1]
if self._port_logical_mode.lower() == "logical":
source_port = src_port[1].split('-')
Expand All @@ -282,7 +283,6 @@ def map_clear_to(self, src_port, dst_port, command_logger=None):

def map_clear(self, src_port, dst_port, command_logger=None):
if self._service_mode.lower() == "tl1":
self._session.send_command("", re_string=self._login_prompt)
if self._port_logical_mode.lower() == "logical":
source_port = src_port[1].split('-')
destination_port = dst_port[1].split('-')
Expand All @@ -300,21 +300,3 @@ def map_clear(self, src_port, dst_port, command_logger=None):

def set_speed_manual(self, command_logger=None):
pass


if __name__ == '__main__':
import sys

from cloudshell.core.logger.qs_logger import get_qs_logger
from common.xml_wrapper import XMLWrapper

ConfigurationParser.set_root_folder(get_file_folder(sys.argv[0].replace("/glimmerglass/", "/")))
gglass = GlimmerglassDriverHandler()
plogger = get_qs_logger('Autoload', 'GlimmerGlass', 'GlimmerGlass')

gglass.login('localhost:1023', 'admin', '********', plogger)
result = gglass.get_resource_description('localhost:1023')
result1 = gglass.get_resource_description('localhost:1023')
print XMLWrapper.get_string_from_xml(result)
print XMLWrapper.get_string_from_xml(result1)

123 changes: 123 additions & 0 deletions glimmerglass/tcp_session.py
@@ -0,0 +1,123 @@
import re
import socket
import time
from collections import OrderedDict

from common.cli.exceptions import SessionLoopLimitException, CommandExecutionException, SessionLoopDetectorException
from common.cli.expect_session import ActionLoopDetector
from common.cli.helper.normalize_buffer import normalize_buffer
from common.cli.tcp_session import TCPSession


class GGTCPSession(TCPSession):
def __init__(self, *args, **kwargs):
super(GGTCPSession, self).__init__(*args, **kwargs)
self._login_prompt = None

def connect(self, host, username, password, command=None, error_map=None, action_map=None, port=None, re_string=''):
self._login_prompt = re_string
return super(GGTCPSession, self).connect(host, username, password, command, error_map, action_map, port, re_string)

def reconnect(self, re_string=''):
return super(GGTCPSession, self).reconnect(self._login_prompt)

def hardware_expect(self, data_str=None, re_string='', expect_map=None, error_map=None,
timeout=None, retries=None, check_action_loop_detector=True, empty_loop_timeout=None,
**optional_args):

"""Get response form the device and compare it to expected_map, error_map and re_string patterns,
perform actions specified in expected_map if any, and return output.
Raise Exception if receive empty responce from device within a minute
:param data_str: command to send
:param re_string: expected string
:param expect_map: dict with {re_str: action} to trigger some action on received string
:param error_map: expected error list
:param timeout: session timeout
:param retries: maximal retries count
:return:
"""

if not expect_map:
expect_map = OrderedDict()

if not error_map:
error_map = OrderedDict()

retries = retries or self._max_loop_retries
empty_loop_timeout = empty_loop_timeout or self._empty_loop_timeout

if data_str is not None:
self._clear_buffer(self._clear_buffer_timeout)

self.logger.info('Command: {}'.format(data_str.replace(self._password, "*" * 7)))
self.send_line(data_str)

if re_string is None or len(re_string) == 0:
raise Exception('ExpectSession', 'List of expected messages can\'t be empty!')

# Loop until one of the expressions is matched or MAX_RETRIES
# nothing is expected (usually used for exit)
output_list = list()
output_str = ''
retries_count = 0
is_correct_exit = False
action_loop_detector = ActionLoopDetector(self._loop_detector_max_action_loops,
self._loop_detector_max_combination_length)

while retries == 0 or retries_count < retries:

try:
read_buffer = self._receive(timeout)
except socket.timeout:
read_buffer = None

if read_buffer:
output_str += read_buffer
retries_count = 0
else:
retries_count += 1
time.sleep(empty_loop_timeout)
continue

if re.search(re_string, output_str, re.DOTALL):
output_list.append(output_str)
is_correct_exit = True

for expect_string in expect_map:
result_match = re.search(expect_string, output_str, re.DOTALL)
if result_match:
output_list.append(output_str)

if check_action_loop_detector:
if action_loop_detector.loops_detected(expect_string):
self.logger.error('Loops detected, output_list: {}'.format(output_list))
raise SessionLoopDetectorException(self.__class__.__name__,
'Expected actions loops detected')
expect_map[expect_string](self)
output_str = ''
break

if is_correct_exit:
break

if not is_correct_exit:
self.logger.debug("Received output: {}".format("".join(output_list)))
raise SessionLoopLimitException(self.__class__.__name__,
'Session Loop limit exceeded, {} loops'.format(retries_count))

result_output = ''.join(output_list)

for error_string in error_map:
result_match = re.search(error_string, result_output, re.DOTALL)
if result_match:
self.logger.error(result_output)
raise CommandExecutionException('ExpectSession',
'Session returned \'{}\''.format(error_map[error_string]))

# Read buffer to the end. Useful when re_string isn't last in buffer
result_output += self._clear_buffer(self._clear_buffer_timeout)

result_output = normalize_buffer(result_output)
self.logger.info(result_output.replace(self._password, "*" * 7))
return result_output

0 comments on commit df7360f

Please sign in to comment.