259 lines
7.5 KiB
Python
259 lines
7.5 KiB
Python
#!/usr/bin/env python3
|
||
|
||
# scripts/config_pfsense.py
|
||
#
|
||
# Import/Export script for vIOS.
|
||
#
|
||
# @author Alain Degreffe <eczema@ecze.com>
|
||
# @copyright 2016 Alain Degreffe
|
||
# @license http://www.gnu.org/licenses/gpl.html
|
||
# @link http://www.eve-ng.net/
|
||
# @version 20181203
|
||
|
||
import getopt, multiprocessing, os, pexpect, re, sys, time
|
||
|
||
conntimeout = 3 # Maximum time for console connection
|
||
expctimeout = 3 # Maximum time for each short expect
|
||
longtimeout = 30 # Maximum time for each long expect
|
||
timeout = 60 # Maximum run time (conntimeout is included)
|
||
|
||
def node_login(handler):
|
||
# Send an empty line, and wait for the login prompt
|
||
i = -1
|
||
while i == -1:
|
||
try:
|
||
handler.sendline('\r\n')
|
||
i = handler.expect([
|
||
'Enter an option:',
|
||
'.*OPNsense.*:~'], timeout = 5)
|
||
except:
|
||
i = -1
|
||
|
||
if i == 0:
|
||
# Need to send username and password
|
||
handler.sendline('8')
|
||
try:
|
||
handler.expect('.*root.*:', timeout = expctimeout)
|
||
return True
|
||
except:
|
||
print('ERROR: error waiting for "root:" prompt.')
|
||
node_quit(handler)
|
||
return False
|
||
|
||
elif i == 1:
|
||
# nothing to do
|
||
return True
|
||
else:
|
||
# Unexpected output
|
||
node_quit(handler)
|
||
return False
|
||
|
||
def node_quit(handler):
|
||
if handler.isalive() == True:
|
||
handler.sendline('exit\n')
|
||
handler.close()
|
||
|
||
def config_get(handler):
|
||
|
||
# Getting the config
|
||
handler.setwinsize(100, 120)
|
||
handler.sendline('cat /conf/config.xml | awk \'{print $0}\'\n')
|
||
#handler.sendline('cat `ls -rt /conf/backup/config-* | tail -1 `\n')
|
||
try:
|
||
handler.expect('</opnsense>', timeout = longtimeout)
|
||
except:
|
||
print('ERROR: error waiting for "#" prompt.')
|
||
node_quit(handler)
|
||
return False
|
||
config = handler.before.decode()
|
||
# Manipulating the config
|
||
config = re.sub('\r', '', config, flags=re.DOTALL) # Unix style
|
||
config = config + '</opnsense>\n';
|
||
config = re.sub('.*<\?xml version=\"1.0\"\?>', '<?xml version=\"1.0\"?>', config, flags=re.DOTALL) # Header
|
||
return config
|
||
|
||
def config_put(handler):
|
||
while True:
|
||
try:
|
||
i = handler.expect('Do you want to set up VLANs now.*', timeout)
|
||
break
|
||
except:
|
||
return False
|
||
handler.sendline('')
|
||
handler.sendline('\n')
|
||
handler.sendline('mount -t cd9660 /dev/cd0 /mnt\n')
|
||
handler.sendline('cp /mnt/config.xml /conf/\n')
|
||
handler.sendline('exit\n')
|
||
while True:
|
||
try:
|
||
i = handler.expect('option:', timeout)
|
||
except:
|
||
return False
|
||
|
||
return True
|
||
|
||
def usage():
|
||
print('Usage: %s <standard options>' %(sys.argv[0]));
|
||
print('Standard Options:');
|
||
print('-a <s> *Action can be:')
|
||
print(' - get: get the startup-configuration and push it to a file')
|
||
print(' - put: put the file as startup-configuration')
|
||
print('-f <s> *File');
|
||
print('-p <n> *Console port');
|
||
print('-t <n> Timeout (default = %i)' %(timeout));
|
||
print('* Mandatory option')
|
||
|
||
def now():
|
||
# Return current UNIX time in milliseconds
|
||
return int(round(time.time() * 1000))
|
||
|
||
def main(action, fiename, port):
|
||
try:
|
||
# Connect to the device
|
||
tmp = conntimeout
|
||
while (tmp > 0):
|
||
handler = pexpect.spawn('telnet 127.0.0.1 %i' %(port))
|
||
time.sleep(0.1)
|
||
tmp = tmp - 0.1
|
||
if handler.isalive() == True:
|
||
break
|
||
|
||
if (handler.isalive() != True):
|
||
print('ERROR: cannot connect to port "%i".' %(port))
|
||
node_quit(handler)
|
||
sys.exit(1)
|
||
|
||
if action == 'get':
|
||
rc = node_login(handler)
|
||
if rc != True:
|
||
print('ERROR: failed to login.')
|
||
node_quit(handler)
|
||
sys.exit(1)
|
||
config = config_get(handler)
|
||
if config in [False, None]:
|
||
print('ERROR: failed to retrieve config.')
|
||
node_quit(handler)
|
||
sys.exit(1)
|
||
|
||
try:
|
||
fd = open(filename, 'a')
|
||
fd.write(config)
|
||
fd.close()
|
||
except:
|
||
print('ERROR: cannot write config to file.')
|
||
node_quit(handler)
|
||
sys.exit(1)
|
||
elif action == 'put':
|
||
rc = config_put(handler)
|
||
if rc != True:
|
||
print('ERROR: failed to push config.')
|
||
node_quit(handler)
|
||
sys.exit(1)
|
||
|
||
# Remove lock file
|
||
lock = '%s/.lock' %(os.path.dirname(filename))
|
||
|
||
if os.path.exists(lock):
|
||
os.remove(lock)
|
||
|
||
# Mark as configured
|
||
configured = '%s/.configured' %(os.path.dirname(filename))
|
||
if not os.path.exists(configured):
|
||
open(configured, 'a').close()
|
||
|
||
node_quit(handler)
|
||
sys.exit(0)
|
||
|
||
except Exception as e:
|
||
print('ERROR: got an exception')
|
||
print(type(e)) # the exception instance
|
||
print(e.args) # arguments stored in .args
|
||
print(e) # __str__ allows args to be printed directly,
|
||
node_quit(handler)
|
||
return False
|
||
|
||
if __name__ == "__main__":
|
||
action = None
|
||
filename = None
|
||
port = None
|
||
|
||
# Getting parameters from command line
|
||
try:
|
||
opts, args = getopt.getopt(sys.argv[1:], 'a:p:t:f:', ['action=', 'port=', 'timeout=', 'file='])
|
||
except getopt.GetoptError as e:
|
||
usage()
|
||
sys.exit(3)
|
||
|
||
for o, a in opts:
|
||
if o in ('-a', '--action'):
|
||
action = a
|
||
elif o in ('-f', '--file'):
|
||
filename = a
|
||
elif o in ('-p', '--port'):
|
||
try:
|
||
port = int(a)
|
||
except:
|
||
port = -1
|
||
elif o in ('-t', '--timeout'):
|
||
try:
|
||
timeout = int(a)
|
||
except:
|
||
timeout = -1
|
||
else:
|
||
print('ERROR: invalid parameter.')
|
||
|
||
# Checking mandatory parameters
|
||
if action == None or port == None or filename == None:
|
||
usage()
|
||
print('ERROR: missing mandatory parameters.')
|
||
sys.exit(1)
|
||
if action not in ['get', 'put']:
|
||
usage()
|
||
print('ERROR: invalid action.')
|
||
sys.exit(1)
|
||
if timeout < 0:
|
||
usage()
|
||
print('ERROR: timeout must be 0 or higher.')
|
||
sys.exit(1)
|
||
if port < 0:
|
||
usage()
|
||
print('ERROR: port must be 32768 or higher.')
|
||
sys.exit(1)
|
||
if action == 'get' and os.path.exists(filename):
|
||
usage()
|
||
print('ERROR: destination file already exists.')
|
||
sys.exit(1)
|
||
if action == 'put' and not os.path.exists(filename):
|
||
usage()
|
||
print('ERROR: source file does not already exist.')
|
||
sys.exit(1)
|
||
if action == 'put':
|
||
try:
|
||
fd = open(filename, 'r')
|
||
config = fd.read()
|
||
fd.close()
|
||
except:
|
||
usage()
|
||
print('ERROR: cannot read from file.')
|
||
sys.exit(1)
|
||
|
||
# Backgrounding the script
|
||
end_before = now() + timeout * 1000
|
||
p = multiprocessing.Process(target=main, name="Main", args=(action, filename, port))
|
||
p.start()
|
||
|
||
while (p.is_alive() and now() < end_before):
|
||
# Waiting for the child process to end
|
||
time.sleep(1)
|
||
|
||
if p.is_alive():
|
||
# Timeout occurred
|
||
print('ERROR: timeout occurred.')
|
||
p.terminate()
|
||
sys.exit(127)
|
||
|
||
if p.exitcode != 0:
|
||
sys.exit(127)
|
||
|
||
sys.exit(0)
|