Skip to content
This repository has been archived by the owner on Nov 10, 2023. It is now read-only.

Refactoring the code #34

Open
selfuryon opened this issue Jun 1, 2019 · 0 comments
Open

Refactoring the code #34

selfuryon opened this issue Jun 1, 2019 · 0 comments
Assignees
Milestone

Comments

@selfuryon
Copy link
Owner

selfuryon commented Jun 1, 2019

Hello!
I want to make the module refactoring.

The first thing I want to separate the connection and the class which knows how to work with device.
The second thing I want to use the Strategy pattern instead of Inheritance due to lack of isolation.

Common class structure:

  • IOConnection as an interface for working for a particular connection
    • As examples of a particular connection are SSHConnection and Telnet Connection
  • DeviceStream adds some logic to basic IOConnection: it understands the end of the output: it recognizes the prompt
  • LayerManager manages all levels of layers or terminal modes
  • Layer is an entity for working with a particular layer or terminal mode
  • Particular set of device type classes (like CiscoIOS, JuniperJunOS) for working with devices. They contain a set of strategy class instances

Try to show the concept of these changes.

Public factory method:

# Public Factory function for creating netdev classes
def create(device_type, connection_type, *args, **kwargs):
	# Create IOConnection separately
	if connection_type == "ssh":
		IOConnection = SSHConnectiom(*args, **kwargs) # As Example for SSHConnection

	# Create DeviceStream and all needed instances for a particular device
	device_stream = DeviceStream(IOConnection)

	# Create Device separately
	if device_type == "cisco_ios":
		layer_manager = LayerManager(device_stream, cisco_checker)
			.add_layer(UserExecMode())
			.add_layer(PrivilegeExecMode())
			.add_layer(ConfigMode())
		return Cisco_IOS(layer_manager)

IOConnection is an abstract class with a public interface which can be used by all particular device type classes.

class IOConnection(abc.ABC):
    """ Abstract IO Connection class"""

    async def connect(self):
        """ Establish Connection """
        pass

    async def disconnect(self):
        pass

    def send(self, cmd):
        """ Send command to stream """
        pass

    async def read(self):
        """ Read from stream """
        pass

IOConnection can be implemented in SSHConnection, TelnetConnection and SerialConnection.

DeviceStream adds some logic to IOConnection. It understands the end of the output for commands: it understands the prompt

class DeviceStream():
	""" Class which know how to work with the device in a stream mode """

	def __init__ (self, IOConnection, prompt_pattern = r"", ):
		self._conn = IOConnection
		self._prompt_pattern = prompt_pattern

	async def send(self, cmd_list):
		pass

	async def read_until(self, pattern, re_flags, until_prompt=True):
		pass

Device type classes are particular classes for working with network devices.

class CiscoIOS():
	""" Abstract Device type"""

	def __init__ (device_stream, layer_manager):
		self._device_stream = device_stream
		self._layer_manager = layer_manager


	async def send_command(self, cmd_list, terminal_mode):
		""" Go to specific terminal mode and run list of commands in there"""
		self._layer_manager.switch_to_layer(terminal_mode)
		self.device_stream(cmd_list)

	async def send_config_set(self, cmd_list):
		""" Go to configuration mode and run list of commands in there"""
		self._layer_manager.switch_to_layer('config_mode')
		self.device_stream(cmd_list)
		self._layer_manager.switch_to_layer('privilege exec')

We have universal class LayerManager for working with terminal modes:

class LayerManager():

    def __init__(self, device_stream, checker_closure):
    	self._device_stream = device_stream
    	self._checker_closure = checker_closure
    	self._current_layer = None

    def add_layer(layer):
    	self._layers[layer.name] = layer
    	return self

    def switch_to_layer(layer_name):
    	if self._current_layer is None:
    		self._current_layer = self.checker_closure()
    	if self._current_layer == layer_name:
    		return
    	# switching to layer

    def commit_transaction():
    	layer = get_layer(self._current_layer)
    	if layer.transactional:
    		layer.committer()

    def get_layer(layer_name):
    	pass

Specific function for checking the cisco like terminal modes:

def cisco_checker(device_stream):
	if ')#':
		return 'config'
	elif '#':
		return 'privilege'
	elif '>':
		return 'user_exec'
	else:
		raise Exeption()

We have universal class Layer for working with particular layer/terminal mode:

class Layer():

	def __init__(name, device_stream, enter, exit, transactional=False, commiter=None):
		self._name = name 
		self._device_stream = device_stream
		self._enter_closure = enter
		self._exit_closure = exit
		self._committer_closure = committer
		self.transactional = transactional

	async def enter(cmd):
		pass

	async def exit(cmd):
		pass

	@atribute
	def get_name():
		return self._name

And cisco like layers:

class ConfigMode(Layer):

	def __init__(name, device_stream, enter, exit, transactional=False, commiter=None):
		super().__init()

	async def enter(cmd='conf t'):
		self._device_stream.send_command(cmd)

	async def exit(cmd='end'):
		self._device_stream.send_command(cmd)

	@atribute
	def get_name():
		return self._name
@selfuryon selfuryon self-assigned this Jun 1, 2019
@selfuryon selfuryon added this to the Release v1.0 milestone Jun 1, 2019
@selfuryon selfuryon mentioned this issue Jun 1, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

1 participant