mirror of
https://github.com/MikroWizard/mikroman.git
synced 2025-06-21 03:55:39 +02:00
Fixed Firmware download from the Mikrotik website when there are multiple npk available Fixed Mikrowizard system permission error when it is set to None Fixed user device group permissions Some minor UI improvements Fix IP scan for one IP scan / Fix not scanning the last IP in the range Fix manual snippet execution not working when device groups are selected Some minor bug fixes and improvements New: Show background tasks and be able to stop them while running in the background (like an IP scanner) Add support for manual MikroWizard update dashboard/settings page update to version 1.0.5 Enhancement: Show permission error in some pages when the user doesn't have permission for that page/action show better charts/graphs in the dashboard and device interface details show more info on the dashboard about update and version information and license
797 lines
29 KiB
Python
797 lines
29 KiB
Python
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# util.py: functions that we need :)
|
|
# MikroWizard.com , Mikrotik router management solution
|
|
# Author: sepehr.ha@gmail.com
|
|
|
|
import pytz
|
|
import datetime
|
|
import time
|
|
import uuid
|
|
import socket
|
|
import config
|
|
from libs.db import db_sysconfig,db_firmware,db_backups,db_events
|
|
from cryptography.fernet import Fernet
|
|
from libs.check_routeros.routeros_check.resource import RouterOSCheckResource
|
|
from libs.check_routeros.routeros_check.helper import RouterOSVersion
|
|
from typing import Dict
|
|
import re
|
|
import json
|
|
import logging
|
|
from libs.red import RedisDB
|
|
from libs.ssh_helper import SSH_Helper
|
|
import os
|
|
from bs4 import BeautifulSoup
|
|
import urllib.request
|
|
import hashlib
|
|
import netifaces
|
|
log = logging.getLogger("util")
|
|
try:
|
|
from libs import utilpro
|
|
ISPRO=True
|
|
except ImportError:
|
|
ISPRO=False
|
|
pass
|
|
import zipfile
|
|
# --------------------------------------------------------------------------
|
|
# date related common methods
|
|
|
|
tz_hki = pytz.timezone("UTC")
|
|
tz_utc = pytz.utc
|
|
|
|
def utc2local(utc_dt, tz=tz_hki):
|
|
"""Convert UTC into local time, given tz."""
|
|
if type(tz) is str:
|
|
tz = pytz.timezone(tz)
|
|
|
|
if not utc_dt:
|
|
return utc_dt
|
|
|
|
d = utc_dt.replace(tzinfo=tz_utc)
|
|
return d.astimezone(tz)
|
|
|
|
def check_port(ip,port):
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
sock.settimeout(0.5)
|
|
result = sock.connect_ex((ip,int(port)))
|
|
sock.close()
|
|
if result == 0:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def crypt_data(text):
|
|
# Encryption: Encrypting password using Fernet symmetric encryption
|
|
cipher_suite = Fernet(config.CRYPT_KEY)
|
|
# Encrypting
|
|
encrypted_password = cipher_suite.encrypt(text.encode()).decode()
|
|
return encrypted_password
|
|
|
|
def decrypt_data(text):
|
|
# Encryption: Decrypting password using Fernet symmetric encryption
|
|
cipher_suite = Fernet(config.CRYPT_KEY)
|
|
# Decrypting password
|
|
decrypted_password = cipher_suite.decrypt(text.encode()).decode()
|
|
return decrypted_password
|
|
|
|
def get_default_user_pass():
|
|
default_user = db_sysconfig.get_default_user().value
|
|
default_pass = db_sysconfig.get_default_password().value
|
|
try:
|
|
default_user=decrypt_data(default_user)
|
|
default_pass=decrypt_data(default_pass)
|
|
except:
|
|
default_user="admin"
|
|
default_pass=""
|
|
return default_user,default_pass
|
|
|
|
def build_api_options(dev):
|
|
default_user,default_pass= get_default_user_pass()
|
|
username=decrypt_data(dev.user_name ) or default_user
|
|
password=decrypt_data(dev.password ) or default_pass
|
|
port=dev.port or 8728
|
|
options={
|
|
'host':dev.ip,
|
|
'username':username,
|
|
'password':password,
|
|
'routeros_version':'auto',
|
|
'port':port,
|
|
'ssl':False
|
|
}
|
|
return options
|
|
|
|
def check_device_firmware_update(dev,q):
|
|
port=dev.port or 8728
|
|
if check_port(dev.ip,port):
|
|
options=build_api_options(dev)
|
|
try:
|
|
is_availbe , current , arch , upgrade_availble = check_update(options)
|
|
except Exception as e:
|
|
q.put({"id": dev.id,"update_availble":False,"reason":"Unknoown Reason"})
|
|
if is_availbe:
|
|
q.put({"id": dev.id,"update_availble":is_availbe,"current_firmware":current,"arch":arch,"upgrade_availble":upgrade_availble})
|
|
else:
|
|
if current:
|
|
q.put({"id": dev.id,"update_availble":is_availbe,"current_firmware":current,"arch":arch,"upgrade_availble":upgrade_availble})
|
|
else:
|
|
q.put({"id": dev.id,"reason":"Wrong user or password"})
|
|
else:
|
|
q.put({"id": dev.id,"update_availble":False,"reason":"Connection problem"})
|
|
|
|
def get_interfaces_counters(router):
|
|
result = {}
|
|
for iface in router.api('/interface/print', stats=True):
|
|
result[iface['name']] = iface
|
|
return result
|
|
|
|
def get_traffic(router,interfaces):
|
|
interfaces.append('aggregate')
|
|
interfaces=",".join(interfaces)
|
|
params = {'interface': interfaces, 'once': b' '}
|
|
results = tuple(router.api('/interface/monitor-traffic', **params))
|
|
traffic={}
|
|
for row in results:
|
|
traffic[row.get('name','total')]={
|
|
'rx-packets-per-second':row.get('rx-packets-per-second',0),
|
|
'rx-bits-per-second':row.get('rx-bits-per-second',0),
|
|
'fp-rx-packets-per-second':row.get('fp-rx-packets-per-second',0),
|
|
'fp-rx-bits-per-second':row.get('fp-rx-bits-per-second',0),
|
|
'rx-drops-per-second':row.get('rx-drops-per-second',0),
|
|
'rx-errors-per-second':row.get('rx-errors-per-second',0),
|
|
'tx-packets-per-second':row.get('tx-packets-per-second',0),
|
|
'tx-bits-per-second':row.get('tx-bits-per-second',0),
|
|
'fp-tx-packets-per-second':row.get('fp-tx-packets-per-second',0),
|
|
'fp-tx-bits-per-second':row.get('fp-tx-bits-per-second',0),
|
|
'tx-drops-per-second':row.get('tx-drops-per-second',0),
|
|
'tx-queue-drops-per-second':row.get('tx-queue-drops-per-second',0),
|
|
'tx-errors-per-second':row.get('tx-errors-per-second',0),
|
|
}
|
|
return traffic
|
|
|
|
def get_interface_list(interfaces):
|
|
interfaces=list(interfaces.keys())
|
|
return interfaces
|
|
|
|
def mergeDictionary(dict_1, dict_2):
|
|
dict_3 = {}
|
|
keys=list(dict_1.keys())
|
|
keys.extend(x for x in list(dict_2.keys()) if x not in keys)
|
|
for key in keys:
|
|
if key in dict_1 and key in dict_2:
|
|
new_key=key
|
|
if dict_1[key].get('default-name',False):
|
|
if dict_1[key]['default-name']!=new_key:
|
|
new_key=dict_1[key].get('default-name',False)
|
|
dict_3[new_key] = {**dict_2[key] , **dict_1[key]}
|
|
else:
|
|
if key in dict_1:
|
|
dict_3[key] = {**dict_1[key]}
|
|
else:
|
|
dict_3[key] = {**dict_2[key]}
|
|
return dict_3
|
|
|
|
def get_network_data(router):
|
|
interfaces=get_interfaces_counters(router)
|
|
interfaces_list=get_interface_list(interfaces)
|
|
traffic=get_traffic(router,interfaces_list)
|
|
return mergeDictionary(interfaces,traffic)
|
|
|
|
def check_or_fix_event(events,eventtype,detail,comment=False):
|
|
if comment:
|
|
found_event_id=next((item for item in events if item["eventtype"] == eventtype and item["detail"] == detail and comment in item["comment"]), False)
|
|
else:
|
|
found_event_id=next((item for item in events if item["eventtype"] == eventtype and item["detail"] == detail), False)
|
|
if found_event_id:
|
|
db_events.fix_event(found_event_id['id'])
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def grab_device_data(dev, q):
|
|
max_attempts = 3
|
|
attempts = 0
|
|
port=dev.port or 8728
|
|
success = False
|
|
time_to_wait=0.1
|
|
while attempts < max_attempts:
|
|
if check_port(dev.ip,port):
|
|
success = True
|
|
break
|
|
attempts += 1
|
|
time.sleep(time_to_wait)
|
|
time_to_wait += 0.1
|
|
if success:
|
|
# get all device events which src is "Data Puller" and status is 0
|
|
events=list(db_events.get_events_by_src_and_status("Data Puller", 0,dev.id).dicts())
|
|
check_or_fix_event(events,"connection","Unreachable")
|
|
options=build_api_options(dev)
|
|
try:
|
|
router=RouterOSCheckResource(options)
|
|
_installed_version=router._get_routeros_version()
|
|
call = router.api.path(
|
|
"/system/resource"
|
|
)
|
|
results = tuple(call)
|
|
result: Dict[str, str] = results[0]
|
|
try:
|
|
call = router.api.path(
|
|
"/system/routerboard"
|
|
)
|
|
routerboard = tuple(call)
|
|
routerboard: Dict[str, str] = routerboard[0]
|
|
result.update(routerboard)
|
|
except Exception as e:
|
|
if 'no such command' not in str(e):
|
|
log.error(e)
|
|
pass
|
|
call = router.api.path(
|
|
"/system/health"
|
|
)
|
|
health = tuple(call)
|
|
|
|
call = router.api.path(
|
|
"/system/identity"
|
|
)
|
|
name = tuple(call)
|
|
name: Dict[str, str] = name[0]
|
|
result.update(name)
|
|
wireless_keys,wireless_data=[],[]
|
|
if ISPRO:
|
|
wireless_keys,wireless_data=utilpro.wireless_actions(router,dev,events)
|
|
try:
|
|
|
|
call = router.api.path(
|
|
"/interface/wireless"
|
|
)
|
|
wifi_results = tuple(call)
|
|
wifi_result: Dict[str, str] = wifi_results[0]
|
|
device_type='router'
|
|
if wifi_result['mode'] in ['ap-bridge','bridge','wds-slave']:
|
|
device_type=wifi_result['mode']
|
|
elif wifi_result['mode'] in ['station','station-wds' , 'station-pseudobridge' , 'station-pseudobridge-clone' , 'station-bridge']:
|
|
device_type='station'
|
|
elif wifi_result['mode'] in ['alignment-only','nstreme-dual-slave']:
|
|
device_type='special'
|
|
else:
|
|
device_type='router'
|
|
except:
|
|
device_type='router'
|
|
except Exception as e:
|
|
log.error(e)
|
|
log.warning(dev.ip)
|
|
q.put({"id": dev.id,"detail":"API Connection","reason":e,"done":False})
|
|
return True
|
|
check_or_fix_event(events,"connection","API Connection")
|
|
try:
|
|
keys=["free-memory","cpu-load","free-hdd-space"]
|
|
if len(health):
|
|
#since routeros v7 they changed health res from api
|
|
excluded_keys=['cpu-overtemp-check','active-fan','fan-mode','heater-control','psu2-state','cpu-overtemp-startup-delay','fan-on-threshold','heater-threshold','use-fan','cpu-overtemp-threshold','fan-switch','psu1-state','state','state-after-reboot']
|
|
if 'type' in health[0]:
|
|
health_vals={}
|
|
for d in health:
|
|
if 'state' in d['name']:
|
|
if d['value'] == 'fail':
|
|
db_events.health_event(dev.id,'Data Puller',d['name'],'Critical',0,"{} is Failed".format(d['name']))
|
|
else:
|
|
check_or_fix_event(events,"health",d['name'])
|
|
continue
|
|
if d['name'] in excluded_keys:
|
|
continue
|
|
health_vals[d['name']]=d['value']
|
|
elif result['board-name']=='x86' or 'x86' in result['architecture-name']:
|
|
health_vals={}
|
|
else:
|
|
health_vals: Dict[str, str] = health[0]
|
|
result.update(health_vals)
|
|
keys.extend(list(health_vals.keys()))
|
|
except Exception as e:
|
|
log.warning(dev.ip)
|
|
log.error(e)
|
|
log.error(health)
|
|
q.put({"id": dev.id,"reason":"Could not health data from device","detail":"Get Health","done":False})
|
|
return True
|
|
check_or_fix_event(events,"connection","Get Health")
|
|
try:
|
|
# arch=result['architecture-name']
|
|
try:
|
|
is_availbe , current , arch , upgrade_availble = check_update(options)
|
|
dev.update_availble=is_availbe
|
|
dev.upgrade_availble=upgrade_availble
|
|
dev.current_firmware=current
|
|
except:
|
|
pass
|
|
force_syslog=True if db_sysconfig.get_sysconfig('force_syslog')=="True" else False
|
|
force_radius=True if db_sysconfig.get_sysconfig('force_radius')=="True" else False
|
|
if force_radius:
|
|
try:
|
|
peer_ip=dev.peer_ip if dev.peer_ip else db_sysconfig.get_sysconfig('default_ip')
|
|
secret = db_sysconfig.get_sysconfig('rad_secret')
|
|
res = configure_radius(router, peer_ip,secret)
|
|
check_or_fix_event(events,"config","radius configuration")
|
|
except:
|
|
db_events.config_event(dev.id,'Data Puller','radius configuration','Error',0,"Force radius Failed")
|
|
pass
|
|
try:
|
|
syslog_configured=check_syslog_config(dev,router,force_syslog)
|
|
if dev.syslog_configured!=syslog_configured:
|
|
dev.syslog_configured=syslog_configured
|
|
check_or_fix_event(events,"config","syslog configuration")
|
|
except:
|
|
db_events.config_event(dev.id,'Data Puller','syslog configuration','Error',0,"Force SysLog Failed")
|
|
pass
|
|
dev.current_firmware=_installed_version
|
|
dev.uptime=result['uptime']
|
|
dev.router_type=device_type
|
|
if dev.name!=result['name']:
|
|
dev.name=result['name']
|
|
if device_type!='router':
|
|
dev.wifi_config=json.dumps(wifi_result)
|
|
|
|
interfaces=get_network_data(router)
|
|
interfaces_keys=interfaces.keys()
|
|
data={}
|
|
for key in keys:
|
|
if key in result:
|
|
data[key]=result[key]
|
|
else:
|
|
data[key]=0
|
|
for intkeys in interfaces_keys:
|
|
keys.extend(["rx-"+intkeys,"tx-"+intkeys,"rxp-"+intkeys,"txp-"+intkeys])
|
|
data["rx-"+intkeys]=interfaces[intkeys]['rx-bits-per-second']
|
|
data["tx-"+intkeys]=interfaces[intkeys]['tx-bits-per-second']
|
|
data["rxp-"+intkeys]=interfaces[intkeys]['rx-packets-per-second']
|
|
data["txp-"+intkeys]=interfaces[intkeys]['tx-packets-per-second']
|
|
|
|
if len(wireless_keys)>0:
|
|
keys.extend(wireless_keys)
|
|
data.update(wireless_data)
|
|
redopts={
|
|
"dev_id":dev.id,
|
|
"keys":keys
|
|
}
|
|
reddb=RedisDB(redopts)
|
|
if not dev.sensors or (len(json.loads(dev.sensors))<len(keys) and dev.sensors!=json.dumps(keys)):
|
|
log.info("updating keys for device {}".format(dev.id))
|
|
dev.sensors=json.dumps(keys)
|
|
reddb.dev_create_keys()
|
|
dev.save()
|
|
reddb.add_dev_data(data)
|
|
check_or_fix_event(events,"connection","DB Write")
|
|
except Exception as e:
|
|
log.error(e)
|
|
log.warning(dev.ip)
|
|
q.put({"id": dev.id,"reason":"Unable to store data in DB","detail":"DB Write","done":False})
|
|
return True
|
|
else:
|
|
q.put({"id": dev.id, "reason":"device not reachable with port {}".format(port),"detail":"Unreachable", "done":False})
|
|
return True
|
|
q.put({"id": dev.id,"done":True,'data':data})
|
|
return True
|
|
|
|
|
|
def check_syslog_config(dev,router,apply=False):
|
|
try:
|
|
if not router:
|
|
options=build_api_options(dev)
|
|
router=RouterOSCheckResource(options)
|
|
peer_ip=dev.peer_ip if dev.peer_ip else db_sysconfig.get_sysconfig('default_ip')
|
|
devid=dev.id
|
|
call = router.api.path(
|
|
"/system/logging/action"
|
|
)
|
|
#create syslog action
|
|
results = tuple(call)
|
|
mikro1=[item for item in results if "mikrowizard" in item.get('name')]
|
|
regex=r'^mikrowizard{}$'.format(devid)
|
|
mikro=[item for item in mikro1 if re.match(regex,item.get('name'))]
|
|
if len(mikro)==1 and mikro[0].get('remote-port')==5014 and mikro[0].get('remote')==peer_ip:
|
|
action_name=mikro[0].get('name')
|
|
else:
|
|
if apply:
|
|
if len(mikro1):
|
|
ids=[item.get('.id') for item in mikro1 if 'mikrowizard' in item.get('name')]
|
|
if len(ids):
|
|
call.remove(*ids)
|
|
action_name='mikrowizard{}'.format(devid)
|
|
action={
|
|
'name':action_name,
|
|
'remote':peer_ip,
|
|
'remote-port':5014,
|
|
'target':'remote'
|
|
}
|
|
res=call.add(**action )
|
|
else:
|
|
return False
|
|
|
|
#create loggings
|
|
call = router.api.path(
|
|
"/system/logging"
|
|
)
|
|
|
|
results = tuple(call)
|
|
|
|
confs=[item for item in results if action_name in item.get('action')]
|
|
if len(confs)!=3:
|
|
if apply:
|
|
ids=[item.get('.id') for item in results if 'mikrowizard' in item.get('prefix')]
|
|
log.error(ids)
|
|
if len(ids):
|
|
call.remove(*ids)
|
|
keys=['critical','error','info']
|
|
for key in keys:
|
|
action={
|
|
'action':action_name,
|
|
'topics':key,
|
|
'prefix':action_name,
|
|
}
|
|
res=call.add(**action )
|
|
else:
|
|
return False
|
|
return True
|
|
except Exception as e:
|
|
log.error(e)
|
|
return False
|
|
|
|
def apply_perm(router,name,perms):
|
|
try:
|
|
#check if radius client is configured and ip ,port,secret is correct
|
|
call = router.api.path(
|
|
"/user/group"
|
|
)
|
|
groups = tuple(call)
|
|
exist=False
|
|
for group in groups:
|
|
if group.get('name')==name:
|
|
exist=group.get('.id')
|
|
p1=group.get('policy').split(',')
|
|
p1.sort()
|
|
if p1==perms:
|
|
return True
|
|
params={
|
|
'name':name,
|
|
'policy':(',').join(perms)
|
|
}
|
|
try:
|
|
if not exist:
|
|
res=call.add(**params)
|
|
else:
|
|
params['.id']=exist
|
|
call.update(**params)
|
|
return True
|
|
if res:
|
|
return True
|
|
except Exception as e:
|
|
log.error(e)
|
|
return False
|
|
except Exception as e:
|
|
log.error(e)
|
|
return False
|
|
|
|
def configure_radius(router,ip,secret):
|
|
try:
|
|
#check if radius client is configured and ip ,port,secret is correct
|
|
call = router.api.path(
|
|
"/radius"
|
|
)
|
|
call2 = router.api.path(
|
|
"/user/aaa"
|
|
)
|
|
radius = tuple(call)
|
|
aaa = tuple(call2)
|
|
if not aaa[0]['use-radius'] or not aaa[0]['accounting'] or not aaa[0]['interim-update']=='0s':
|
|
params={
|
|
'use-radius':True,
|
|
'accounting':True,
|
|
'interim-update':'0s'
|
|
}
|
|
tuple(router.api.path('user', 'aaa')('set', **params))
|
|
for res in radius:
|
|
if res.get('address')==ip and res.get('secret')==secret:
|
|
return True
|
|
|
|
#configure radius client
|
|
action={
|
|
'address':ip,
|
|
'secret':secret,
|
|
'service':'login',
|
|
'require-message-auth':'no'
|
|
}
|
|
try:
|
|
call.add(**action)
|
|
except:
|
|
action.pop('require-message-auth')
|
|
call.add(**action)
|
|
|
|
return True
|
|
except Exception as e:
|
|
log.error(e)
|
|
return False
|
|
|
|
def FourcePermToRouter(dev,perm):
|
|
try:
|
|
options=build_api_options(dev)
|
|
router=RouterOSCheckResource(options)
|
|
peer_ip=dev.peer_ip if dev.peer_ip else db_sysconfig.get_sysconfig('default_ip')
|
|
secret = db_sysconfig.get_sysconfig('rad_secret')
|
|
res = configure_radius(router, peer_ip,secret)
|
|
try:
|
|
pl=json.loads(perm[0].perm_id.perms)
|
|
perms=[p if pl[p] else '!{}'.format(p) for p in pl]
|
|
perms.sort()
|
|
_installed_version=router._get_routeros_version()
|
|
if _installed_version > RouterOSVersion('7.6'):
|
|
if "!dude" in perms:
|
|
perms.remove("!dude")
|
|
elif "dude" in perms:
|
|
perms.remove("dude")
|
|
if _installed_version > RouterOSVersion('7.2'):
|
|
if "!tikapp" in perms:
|
|
perms.remove("!tikapp")
|
|
elif "tikapp" in perms:
|
|
perms.remove("tikapp")
|
|
if _installed_version < RouterOSVersion('7.1'):
|
|
if "!rest-api" in perms:
|
|
perms.remove("!rest-api")
|
|
elif "rest-api" in perms:
|
|
perms.remove("rest-api")
|
|
res2=apply_perm(router,perm[0].perm_id.name,perms)
|
|
return res2
|
|
except Exception as e:
|
|
log.error(e)
|
|
pass
|
|
return False
|
|
except Exception as e:
|
|
log.error(e)
|
|
return False
|
|
|
|
def check_update(options,router=False):
|
|
ofa=db_sysconfig.get_firmware_action().value
|
|
#is_availbe , current , arch , data
|
|
try:
|
|
if not router:
|
|
router=RouterOSCheckResource(options)
|
|
_installed_version=router._get_routeros_version()
|
|
try:
|
|
if ofa=="keep" and _installed_version < RouterOSVersion('6.99.99'):
|
|
_latest_version=RouterOSVersion(db_sysconfig.get_firmware_old().value)
|
|
else:
|
|
_latest_version=RouterOSVersion(db_sysconfig.get_firmware_latest().value)
|
|
except:
|
|
_latest_version=False
|
|
call = router.api.path(
|
|
"/system/resource"
|
|
)
|
|
results = tuple(call)
|
|
result: Dict[str, str] = results[0]
|
|
arch=result['architecture-name']
|
|
try:
|
|
call = router.api.path(
|
|
"/system/routerboard"
|
|
)
|
|
|
|
routerboard = tuple(call)
|
|
routerboard: Dict[str, str] = routerboard[0]
|
|
result.update(routerboard)
|
|
except Exception as e:
|
|
if 'no such command' not in str(e):
|
|
log.error(e)
|
|
pass
|
|
upgrade=False
|
|
if 'x86' not in result['board-name'] and result['current-firmware']!= result['upgrade-firmware'] and result['board-name']!='x86':
|
|
upgrade=True
|
|
if _latest_version and _installed_version < _latest_version:
|
|
return True, _installed_version,arch,upgrade
|
|
else:
|
|
return False, _installed_version,arch,upgrade
|
|
except Exception as e:
|
|
log.error("Error during firmware check for host : {}".format(options["host"]))
|
|
log.error(e)
|
|
return False,False,False,False
|
|
|
|
def log_alert(type,dev,massage):
|
|
log.error("Alert: {} {} Device: {} ".format(type,massage,dev.ip))
|
|
|
|
def backup_routers(dev,q):
|
|
status=backup_router(dev)
|
|
q.put({"id": dev.id,"state":status})
|
|
|
|
def run_snippets(dev, snippet,q):
|
|
result=run_snippet(dev, snippet)
|
|
q.put({"devid": dev.id,"devip": dev.ip,"devname": dev.name, "status":True if result else False , "result":result if result else 'Exec Failed'})
|
|
return result
|
|
|
|
def run_snippet(dev, snippet):
|
|
port=dev.port or 8728
|
|
try:
|
|
if check_port(dev.ip,port):
|
|
options=build_api_options(dev)
|
|
options['timeout']=120
|
|
#check ssh service status
|
|
router=RouterOSCheckResource(options)
|
|
options['router']=router
|
|
call = router.api.path(
|
|
"/ip/service"
|
|
)
|
|
results = tuple(call)
|
|
ssh_info={}
|
|
for res in results:
|
|
if res['name'] == 'ssh':
|
|
ssh_info['disabled']=res['disabled']
|
|
ssh_info['.id']=res['.id']
|
|
options['ssh_port']=res['port']
|
|
break
|
|
#enable ssh if disabled
|
|
if ssh_info['disabled']:
|
|
#ssh is disabled we need to enable it
|
|
params = {'disabled': False, '.id' : ssh_info['.id']}
|
|
call.update(**params)
|
|
try:
|
|
ssh=SSH_Helper(options)
|
|
result=ssh.exec_command(snippet)
|
|
if not result:
|
|
result="executed successfully"
|
|
except Exception as e:
|
|
log.error(e)
|
|
log_alert('ssh',dev,'During backup ssh error')
|
|
if ssh_info['disabled']:
|
|
#undo ssh config after finishing backup
|
|
params = {'disabled': True, '.id' : ssh_info['.id']}
|
|
call.update(**params)
|
|
return result
|
|
else:
|
|
log_alert('connection',dev,'During backup error with connectiong to api')
|
|
return False
|
|
except Exception as e:
|
|
log.error(e)
|
|
log_alert('backup',dev,'Problem During backup when connecting to ssh')
|
|
return False
|
|
|
|
def backup_router(dev):
|
|
port=dev.port or 8728
|
|
try:
|
|
if check_port(dev.ip,port):
|
|
options=build_api_options(dev)
|
|
options['timeout']=120
|
|
#check ssh service status
|
|
router=RouterOSCheckResource(options)
|
|
options['router']=router
|
|
call = router.api.path(
|
|
"/ip/service"
|
|
)
|
|
results = tuple(call)
|
|
ssh_info={}
|
|
for res in results:
|
|
if res['name'] == 'ssh':
|
|
ssh_info['disabled']=res['disabled']
|
|
ssh_info['.id']=res['.id']
|
|
options['ssh_port']=res['port']
|
|
break
|
|
#enable ssh if disabled
|
|
if ssh_info['disabled']:
|
|
#ssh is disabled we need to enable it
|
|
params = {'disabled': False, '.id' : ssh_info['.id']}
|
|
call.update(**params)
|
|
try:
|
|
ssh=SSH_Helper(options)
|
|
configs=ssh.get_config()
|
|
state=store_config(dev,configs)
|
|
|
|
except Exception as e:
|
|
log.error(e)
|
|
log_alert('ssh',dev,'During backup ssh error')
|
|
if ssh_info['disabled']:
|
|
#ssh is disabled we need to enable it
|
|
params = {'disabled': True, '.id' : ssh_info['.id']}
|
|
call.update(**params)
|
|
return True
|
|
else:
|
|
log_alert('connection',dev,'During backup error with connectiong to api')
|
|
return False
|
|
except Exception as e:
|
|
log.error(e)
|
|
log_alert('backup',dev,'Problem During backup when connecting to ssh')
|
|
return False
|
|
|
|
def store_config(dev,configs):
|
|
dir=config.BACKUP_DIR
|
|
#add device mac and curent date to dir
|
|
identifier=dev.mac
|
|
if identifier=='tunnel':
|
|
identifier=identifier+"_devid_"+(str(dev.id))
|
|
dir=dir+identifier+"/"+datetime.datetime.now().strftime("%Y-%m-%d")+"/"
|
|
filename=datetime.datetime.now().strftime("%H-%M-%S")+".txt"
|
|
filedir=dir+filename
|
|
try:
|
|
if not os.path.exists(dir):
|
|
os.makedirs(dir)
|
|
#store config file
|
|
with open(filedir, "w") as text_file:
|
|
text_file.write(configs)
|
|
#add record to db
|
|
db_backups.create(
|
|
dev=dev,
|
|
directory=filedir,
|
|
size=os.path.getsize(filedir),
|
|
)
|
|
return True
|
|
except Exception as e:
|
|
log.error(e)
|
|
log_alert('backup',dev,'Problem During backup when saving file')
|
|
return False
|
|
|
|
|
|
def get_ethernet_wifi_interfaces():
|
|
interfaces = netifaces.interfaces()
|
|
ethernet_wifi_interfaces = []
|
|
interfaces.sort()
|
|
for interface in interfaces:
|
|
try:
|
|
addr = netifaces.ifaddresses(interface)
|
|
if 17 in addr.keys():
|
|
if re.match(r'(en|wl|eth).*',interface):
|
|
ethernet_wifi_interfaces.append({'interface':interface
|
|
,'mac':addr[17][0]['addr']})
|
|
except Exception as e:
|
|
log.error(e)
|
|
pass
|
|
return ethernet_wifi_interfaces
|
|
|
|
def generate_serial_number(interfaces):
|
|
mac_addresses = []
|
|
for interface in interfaces:
|
|
try:
|
|
mac_addresses.append(interface['mac'])
|
|
except Exception as e:
|
|
pass
|
|
if len(mac_addresses)>0:
|
|
# Sort the MAC addresses to ensure consistent ordering
|
|
mac_addresses.sort()
|
|
# Concatenate the MAC addresses into a single string
|
|
mac_string = ''.join(mac_addresses)
|
|
# Generate a UUID based on the MAC string
|
|
hwid = "mw" + str(uuid.uuid5(uuid.NAMESPACE_DNS, mac_string))
|
|
return str(hwid)
|
|
else:
|
|
return None
|
|
|
|
def sizeof_fmt(num, suffix="B"):
|
|
for unit in ("", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"):
|
|
if abs(num) < 1024.0:
|
|
return f"{num:3.1f}{unit}{suffix}"
|
|
num /= 1024.0
|
|
return f"{num:.1f}Yi{suffix}"
|
|
|
|
def get_local_users(opts,router=False,full=False):
|
|
try:
|
|
if not router:
|
|
router=RouterOSCheckResource(opts)
|
|
call = router.api.path(
|
|
"/user"
|
|
)
|
|
if not full:
|
|
results=[a['name'] for a in tuple(call)]
|
|
else:
|
|
results=tuple(call)
|
|
return results
|
|
except Exception as e:
|
|
log.error(e)
|
|
return False
|
|
|
|
def delete_file(file):
|
|
try:
|
|
#check if file exist:
|
|
if not os.path.exists(file):
|
|
return True
|
|
os.remove(file)
|
|
return True
|
|
except Exception as e:
|
|
log.error(e)
|
|
return False
|
|
def ispro():
|
|
return ISPRO
|
|
|
|
if __name__ == '__main__':
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
|