Following the Mikrotik wiki here: http://wiki.mikrotik.com/wiki/Manual:API
Using Python to add a bridge and enable spanning tree on it.
For example
rez = apiros.runCommand(“interface/bridge/add”, name = “Trunk”, protocol-mode = “stp”)
Gives an error of:
“There is an error in your expression: ***keyword can’t be an expression”
NB. The code has been a little modified, I am using IDLE.
'''
Created on Oct 18, 2013
@author: dmcken
Initially copied from http://wiki.mikrotik.com/wiki/Manual:API#Example_client
'''
import pprint
import sys, time, md5, binascii, socket, select
class APIError(Exception):
def __init__(self, message, category = -1):
self.message = message
self.category = category
def __str__(self):
return repr(self.message)
class ApiRos:
"""
Routeros api
"""
def __init__(self, ip):
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sk.connect((ip, 8728))
self.sk = sk
self.currenttag = 0
def login(self, username, pwd):
for _, attrs in self.talk(["/login"]):
chal = binascii.unhexlify(attrs['=ret'])
md = md5.new()
md.update('\x00')
md.update(pwd)
md.update(chal)
self.talk(["/login", "=name=" + username,
"=response=00" + binascii.hexlify(md.digest())])
def talk(self, words):
if self.writeSentence(words) == 0:
return
r = []
while 1:
i = self.readSentence();
if len(i) == 0:
continue
reply = i[0]
attrs = {}
for w in i[1:]:
j = w.find('=', 1)
if (j == -1):
attrs[w] = ''
else:
attrs[w[:j]] = w[j + 1:]
r.append((reply, attrs))
if reply == '!done':
return r
def writeSentence(self, words):
ret = 0
for w in words:
self.writeWord(w)
ret += 1
self.writeWord('')
return ret
def readSentence(self):
r = []
while 1:
w = self.readWord()
if w == '':
return r
r.append(w)
def writeWord(self, w):
# print "<<< " + w
self.writeLen(len(w))
self.writeStr(w)
def readWord(self):
ret = self.readStr(self.readLen())
# print ">>> " + ret
return ret
def writeLen(self, l):
if l < 0x80:
self.writeStr(chr(l))
elif l < 0x4000:
l |= 0x8000
self.writeStr(chr((l >> 8) & 0xFF))
self.writeStr(chr(l & 0xFF))
elif l < 0x200000:
l |= 0xC00000
self.writeStr(chr((l >> 16) & 0xFF))
self.writeStr(chr((l >> 8) & 0xFF))
self.writeStr(chr(l & 0xFF))
elif l < 0x10000000:
l |= 0xE0000000
self.writeStr(chr((l >> 24) & 0xFF))
self.writeStr(chr((l >> 16) & 0xFF))
self.writeStr(chr((l >> 8) & 0xFF))
self.writeStr(chr(l & 0xFF))
else:
self.writeStr(chr(0xF0))
self.writeStr(chr((l >> 24) & 0xFF))
self.writeStr(chr((l >> 16) & 0xFF))
self.writeStr(chr((l >> 8) & 0xFF))
self.writeStr(chr(l & 0xFF))
def readLen(self):
c = ord(self.readStr(1))
if (c & 0x80) == 0x00:
pass
elif (c & 0xC0) == 0x80:
c &= ~0xC0
c <<= 8
c += ord(self.readStr(1))
elif (c & 0xE0) == 0xC0:
c &= ~0xE0
c <<= 8
c += ord(self.readStr(1))
c <<= 8
c += ord(self.readStr(1))
elif (c & 0xF0) == 0xE0:
c &= ~0xF0
c <<= 8
c += ord(self.readStr(1))
c <<= 8
c += ord(self.readStr(1))
c <<= 8
c += ord(self.readStr(1))
elif (c & 0xF8) == 0xF0:
c = ord(self.readStr(1))
c <<= 8
c += ord(self.readStr(1))
c <<= 8
c += ord(self.readStr(1))
c <<= 8
c += ord(self.readStr(1))
return c
def writeStr(self, str):
n = 0;
while n < len(str):
r = self.sk.send(str[n:])
if r == 0: raise RuntimeError, "connection closed by remote end"
n += r
def readStr(self, length):
ret = ''
while len(ret) < length:
s = self.sk.recv(length - len(ret))
if s == '': raise RuntimeError, "connection closed by remote end"
ret += s
return ret
def getSocket(self):
return self.sk
def runCommand(self, command, **arguments):
'''
Run a command attempting to keep as close to the command line version,
while maintaining an easy to use library.
'''
apiMessage = [command]
if arguments != None:
apiMessage += ["={0}={1}".format(k, v) for k, v in arguments.items()]
rez = self.talk(apiMessage)
# print rez
# Remove the !done at the end of the list.
if rez[len(rez) - 1][0] == '!done':
doneVal = rez.pop()
# Check for error conditions (Need to make this more efficient).
trapVal = filter(lambda x: x[0] == '!trap', rez)
if trapVal != []:
trapVal = trapVal.pop()
if 'category' in trapVal:
category = trapVal[1]['=category']
else:
category = -1
# print "TrapVal = {0}".format(trapVal[1])
raise APIError(trapVal[1]['=message'], category)
# Extract the data itself
data = map(lambda x: x[1], rez)
if data == []:
if doneVal[1] != {}:
data = doneVal[1]['=ret']
return data
if __name__ == '__main__':
apiros = ApiRos('172.18.20.2')
apiros.login('admin', '')
rez = apiros.runCommand("interface/bridge/add", name = "Trunk", protocol-mode = "stp")
pprint.pprint(rez)