<reponame>ondrejholecek/fortimonitor<filename>lib/ScriptCommands.py
from lib.ScriptFile import ScriptFile
import json
import datetime
import pytz
import re

###
### This is the alternative class to SSHCommands.
### It tries to simulate the behavior of SSHCommands class,
### but reads the outputs from the JSONline file instead
### of the ssh connection.
###

class ScriptCommands:
	def __init__(self, filename):
		self.filename = filename
		self.script   = ScriptFile(filename)

		self.re_status_hostname = re.compile("^Hostname:\s*(.*)$", re.M)
		self.re_status_vdoms    = re.compile("^Virtual domain configuration:\s*(.*)$", re.M)
		self.re_status_mgmtvdom = re.compile("^Current virtual domain:\s*(.*)$", re.M)
		self.re_status_version  = re.compile('(\S)\s+Version:\s+(.*?)\s+v(\d+)\.(\d+)\.(\d+),build(\d+),(\d+)') # does not start with ^ because the is prompt
		                                                                                                        # and we also use it to get the prompt char.
		self.re_status_serial   = re.compile('^Serial-Number:\s*(.*)$', re.M)
		self.re_status_module_serial = re.compile('^Module Serial-Number:\s*(.*)$', re.M)

		self.local_params = {
			'time_offset_seconds': None,
		}

		self.info = {}
		self.info['hostname']      = None
		self.info['vdoms_enabled'] = None
		self.info['prompt']        = None
		self.info['mgmt_vdom']     = None
		self.info['device']        = None
		self.info['serial']        = None
		self.info['version']       = {
			'major': None,
			'minor': None,
			'patch': None,
			'build': None,
			'compilation': None,
		}

		self.last_used_timestamp = 0

		self.basics()

	def is_live(self):
		return False

	def set_local_param(self, name, value):
		self.local_params[name] = value
	
	def get_local_param(self, name):
		return self.local_params[name]

	def destroy(self):
		return

	def basics(self):
		# read automatic command which should be at the beginning of the file
		obj = self.clever_exec(None)
		#print json.dumps(obj, indent=4)

		# fill something from the file direectly
		self.info['connected_on']       = obj['info']['connected_on']
		self.info['nonce']              = obj['info']['nonce']
		self.info['hostname_extension'] = obj['info']['hostname_extension']
		self.info['prompt_character']   = obj['info']['prompt_character']

		# read get system status output
		gss = None
		for cmd in obj['internal_commands']:
			if cmd ['command'] == 'get system status':
				gss = obj['info']['prompt'] + cmd['output']

		if gss == None:
			raise Exception("File does not contain 'get system status' in automatic commands section")

		self.parse_system_status(gss)

		# save the time offset as it was during the data collection
		self.local_params['time_offset_seconds'] = obj['flags']['time_offset'] * 60

	def parse_system_status(self, gss):
		g = self.re_status_version.search(gss)
		if not g: 
			raise Exception("Cannot find version in 'get system status' output")
		else:
			self.info['device'] = g.group(2)
			self.info['version']['major'] = int(g.group(3))
			self.info['version']['minor'] = int(g.group(4))
			self.info['version']['patch'] = int(g.group(5))
			self.info['version']['build'] = int(g.group(6))
			self.info['version']['compilation'] = int(g.group(7))

		g1 = self.re_status_module_serial.search(gss)
		g2 = self.re_status_serial.search(gss)
		if g1:
			self.info['serial'] = g1.group(1)
		elif g2:
			self.info['serial'] = g2.group(1)
		else:
			raise Exception("Cannot find serial number in 'get system status' output")

		g = self.re_status_hostname.search(gss)
		if not g: 
			raise Exception("Cannot find hostname in 'get system status' output")
		else:
			self.info['hostname'] = g.group(1)

		g = self.re_status_mgmtvdom.search(gss)
		if not g: 
			raise Exception("Cannot find management vdom in 'get system status' output")
		else:
			self.info['mgmt_vdom'] = g.group(1)

		g = self.re_status_vdoms.search(gss)
		if not g: 
			raise Exception("Cannot find vdoms configuration in 'get system status' output")

		if 'enable' in g.group(1):
			self.info['vdoms_enabled'] = True
		elif 'disable' in g.group(1):
			self.info['vdoms_enabled'] = False
		else:
			raise Exception("Cannot find out whether VDOMs are enabled in 'get system status' output")
			
		hostname = self.info['hostname']
		if self.info['hostname_extension'] != None:
			hostname += self.info['hostname_extension']

		if self.info['vdoms_enabled']:
			self.info['prompt'] = "%s (global) %s " % (hostname, self.info['prompt_character'],)
		else:
			self.info['prompt'] = "%s %s " % (hostname, self.info['prompt_character'],)

	def get_parsed(self, parser, params):
		while True:
			obj = self.script.next()
			if obj == None: 
				raise KeyboardInterrupt()

			if obj['info']['type'] != 'parser': continue
			if obj['info']['parser_name'] != parser: continue
			if obj['info']['parser_input'] != list(params): continue

			return obj['output']

	def get_last_timestamp(self):
		return self.last_used_timestamp

	def special_commands(self, command, vdom):
		if command in ('exe date', 'exe time'):
			d = pytz.UTC.localize(datetime.datetime.utcfromtimestamp(self.last_used_timestamp))

			time_offset = self.get_local_param('time_offset_seconds')
			if time_offset != None:
				d += datetime.timedelta(seconds=time_offset)
				
			if command == 'exe date':
				return d.strftime("current date is: %Y-%m-%d\n")
			elif command == 'exe time':
				return d.strftime("current time is: %H:%M:%S\nlast ntp sync:%a %m %d %H:%M:%S %Y\n")

		return None

	def clever_exec(self, command, vdom=None):
		special = self.special_commands(command, vdom)	
		if special != None: return special

		while True:
			obj = self.script.next()
			if obj == None: 
				raise KeyboardInterrupt()
	
			if obj['command'] == command and obj['context'] == vdom:
				self.last_used_timestamp = obj['timestamp']
				return obj['output']

	def continuous_exec(self, command, divide_callback, result_callback, exit_callback, args={}, vdom=None, simulate=None):
		raise Exception("Continuous commands from script file are not supported yet")
