ulgbird.py 23.2 KB
Newer Older
1 2 3
#!/usr/bin/env python
#
# ULG - Universal Looking Glass
4
# (C) 2012 CZ.NIC, z.s.p.o.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


# Imports
import os
import socket
import re
24
import pexpect
25
import hashlib
26 27 28 29

import defaults

import ulgmodel
Tomas Hlavacek's avatar
Tomas Hlavacek committed
30
import ulggraph
31

32
IPV46_SUBNET_REGEXP = '^[0-9a-fA-F:\.]+(/[0-9]{1,2}){0,1}$'
33 34
RTNAME_REGEXP = '^[a-zA-Z0-9]+$'
STRING_SYMBOL_ROUTING_TABLE = 'routing table'
35

36 37 38
STRING_EXPECT_SSH_NEWKEY='Are you sure you want to continue connecting'
STRING_EXPECT_PASSWORD='(P|p)assword:'
STRING_EXPECT_SHELL_PROMPT_REGEXP = '\n[a-zA-Z0-9\._-]+>'
39
STRING_EXPECT_REPLY_START = 'BIRD\s+[^\s]+\s+ready\.'
40 41
STRING_LOGOUT_COMMAND = 'exit'

42
BIRD_SOCK_HEADER_REGEXP='^([0-9]+)[-\s](.+)$'
43
BIRD_SOCK_REPLY_END_REGEXP='^([0-9]+)\s*(\s.*)?$'
44

45 46 47
BIRD_CONSOLE_PROMPT_REGEXP='[^>]+>\s*'


48 49
BIRD_SHOW_PROTO_LINE_REGEXP='^\s*([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)(\s+([^\s].+)){0,1}\s*$'
BIRD_SHOW_PROTO_HEADER_REGEXP='^\s*(name)\s+(proto)\s+(table)\s+(state)\s+(since)\s+(info)\s*$'
50

51
BIRD_RT_LINE_REGEXP = '^([^\s]+)\s+via\s+([^\s]+)\s+on\s+([^\s]+)\s+(\[[^\]]+\])\s+(\*?)\s*([^\s]+)\s+([^\s]+)'
52
BIRD_ASFIELD_REGEXP = '^\s*\[AS([0-9]+)(i|\?)\]\s*$'
53

Tomas Hlavacek's avatar
Tomas Hlavacek committed
54 55 56
BIRD_SHOW_SYMBOLS_LINE_REGEXP = '^([^\s]+)\s+(.+)\s*'

BIRD_GRAPH_SH_ROUTE_ALL = "Graph show route table <RT> for <IP subnet>"
57

58 59
bird_sock_header_regexp = re.compile(BIRD_SOCK_HEADER_REGEXP)
bird_sock_reply_end_regexp = re.compile(BIRD_SOCK_REPLY_END_REGEXP)
60
bird_rt_line_regexp = re.compile(BIRD_RT_LINE_REGEXP)
61
bird_asfield_regexp = re.compile(BIRD_ASFIELD_REGEXP)
62
bird_show_symbols_line_regexp = re.compile(BIRD_SHOW_SYMBOLS_LINE_REGEXP)
63
bird_show_proto_line_regexp = re.compile(BIRD_SHOW_PROTO_LINE_REGEXP)
64

65
BIRD_SH_ROUTE_ALL_ASES_REGEXP = "^(\s*BGP\.as_path:\s+)([0-9\s]+)\s*$"
Tomas Hlavacek's avatar
Tomas Hlavacek committed
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
bird_sh_route_all_ases_regexp = re.compile(BIRD_SH_ROUTE_ALL_ASES_REGEXP)

BIRD_SH_ROUTE_ALL_NEXTHOP_REGEXP = ".*\s+via\s+([0-9a-fA-F:\.]+)\s+on\s+[^\s]+\s+\[([^\s]+)\s+.*"
bird_sh_route_all_nexthop_regexp = re.compile(BIRD_SH_ROUTE_ALL_NEXTHOP_REGEXP)

BIRD_SH_ROUTE_ALL_USED_REGEXP = ".*\]\s+\*\s+\(.*"
bird_sh_route_all_used_regexp = re.compile(BIRD_SH_ROUTE_ALL_USED_REGEXP)

def bird_parse_sh_route_all(text,prependas):
    def split_ases(ases):
		return str.split(ases)

    DEFAULT_PARAMS = {'recuse':False, 'reconly':False, 'aggr':None}

    res = []
81
    params = dict(DEFAULT_PARAMS)
Tomas Hlavacek's avatar
Tomas Hlavacek committed
82 83 84 85 86 87 88 89 90
    for l in str.splitlines(text):
        m = bird_sh_route_all_nexthop_regexp.match(l)
        if(m):
            params['peer'] = m.group(2)
            if(bird_sh_route_all_used_regexp.match(l)):
                params['recuse'] = True

        m = bird_sh_route_all_ases_regexp.match(l)
        if(m):
91
            ases = [ulgmodel.annotateAS("AS"+str(asn)) for asn in [prependas] + split_ases(m.group(2))]
Tomas Hlavacek's avatar
Tomas Hlavacek committed
92
            res.append((ases,params))
93
            params = dict(DEFAULT_PARAMS)
Tomas Hlavacek's avatar
Tomas Hlavacek committed
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
            continue

    return res

def bird_reduce_paths(paths):
    def assign_value(path):
        if(path[1]['recuse']):
            return 1
        elif(path[1]['reconly']):
            return 100
        else:
            return 10

    return sorted(paths,key=assign_value)

109
def parseBirdShowProtocols(text):
110
    def parseShowProtocolsLine(line):
111
        m = bird_show_proto_line_regexp.match(line)
112 113 114 115
        if(m):
            res = list(m.groups()[0:5])
            if(m.group(6)):
                res.append(m.group(6))
116

117 118
            return res
        else:
119 120
#            skip silently the bgp log
#            ulgmodel.log("WARN: bird.parseShowProtocolsLine failed to match line: "+line)
121
            return None
122

123 124 125

    header = []
    table = []
126

127 128 129 130 131 132 133 134 135 136 137
    for l in str.splitlines(text):
        if(re.match('^\s*$',l)):
            continue
        
        hm = re.match(BIRD_SHOW_PROTO_HEADER_REGEXP,l)
        if(hm):
            header = hm.groups()
        else:
            pl = parseShowProtocolsLine(l)
            if(pl):
                table.append(pl)
138 139
#            else:
#                ulgmodel.log("ulgbird.parseBirdShowProtocols skipping unparsable line: "+l)
140

141
    return (header,table,len(table))
142 143 144 145 146 147

# classes

class BirdShowProtocolsCommand(ulgmodel.TextCommand):
    COMMAND_TEXT = 'show protocols'

148
    def __init__(self,name=None,show_proto_all_command=None,proto_filter=None):
149
        ulgmodel.TextCommand.__init__(self,self.COMMAND_TEXT,param_specs=[],name=name)
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
        self.show_proto_all_command = show_proto_all_command
        self.fltr = proto_filter

    def _getPeerURL(self,decorator_helper,router,peer_id):
        if decorator_helper and self.show_proto_all_command:
            return decorator_helper.getRuncommandURL({'routerid':str(decorator_helper.getRouterID(router)),
                                                      'commandid':str(decorator_helper.getCommandID(router,self.show_proto_all_command)),
                                                      'param0':peer_id})
        else:
            return None

    def _getPeerTableCell(self,decorator_helper,router,peer_id):
        url = self._getPeerURL(decorator_helper,router,peer_id)
        if(url):
            return decorator_helper.ahref(url,peer_id)
        else:
            return peer_id
167

168
    def _decorateTableLine(self,table_line,router,decorator_helper):
169 170 171 172 173 174 175 176 177
        def _getTableLineColor(state):
            if(state == 'up'):
                return ulgmodel.TableDecorator.GREEN
            elif(state == 'start'):
                return ulgmodel.TableDecorator.YELLOW
            else:
                return ulgmodel.TableDecorator.RED

        color = _getTableLineColor(table_line[3])
178 179 180 181 182 183 184 185 186 187
        tl = [(self._getPeerTableCell(decorator_helper,router,table_line[0]),color),
              (table_line[1],color),
              (table_line[2],color),
              (table_line[3],color),
              (table_line[4],color),
              ]
        if(len(table_line)>5):
            tl.append((table_line[5],color))

        return tl
188

189

190 191 192 193
    def decorateResult(self,session,decorator_helper=None):
        if(not session):
            raise Exception("Can not decorate result without valid session.")

194 195 196
	if(session.getResult() == None):
            return (decorator_helper.pre(defaults.STRING_EMPTY), 1)

197 198
        if((not session.getRouter()) or (not decorator_helper)):
            return "<pre>\n%s\n</pre>" % session.getResult()
199
        else:
200
            pr = parseBirdShowProtocols(session.getResult())
201
            table_header = pr[0]
202 203
            table = []

204
            for tl in pr[1][session.getRange():session.getRange()+defaults.range_step]:
205 206 207
                # skip when there is a filter and it does not match the protocol type
                if(self.fltr) and (not re.match(self.fltr,tl[1])):
                    continue
208
                table.append(self._decorateTableLine(tl,session.getRouter(),decorator_helper))
209

210
            return (ulgmodel.TableDecorator(table,table_header).decorate(),pr[2])
211 212


213
class AbstractBGPPeerSelectCommand(ulgmodel.TextCommand):
214
    """ Abstract class for all BIRD BGP peer-specific commands """
215

216 217
    def __init__(self,router,name=None):
        ulgmodel.TextCommand.__init__(self,self.COMMAND_TEXT,param_specs=[router.getBGPPeerSelect()],name=name)
218

219 220

class BirdShowProtocolsAllCommand(AbstractBGPPeerSelectCommand):
221 222 223
    COMMAND_TEXT = 'show protocols all %s'


224

225 226


227
class AbstractRouteTableCommand(ulgmodel.TextCommand):
228

229 230 231 232
    def _decorateOriginAS(self,asfield,decorator_helper):
        # expected input is "[AS28171i]"
        m = bird_asfield_regexp.match(asfield)
        if(m):
233
            return '['+decorator_helper.decorateASN(m.group(1))+m.group(2)+']'
234 235 236
        else:
            return asfield

237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
    def _genTable(self,table_lines,decorator_helper,router):
        def matchBIRDBGPRTLine(line):
            m = bird_rt_line_regexp.match(line)
            if(m):
                return m.groups()
            else:
                ulgmodel.debug("BirdShowRouteProtocolCommand: Can not parse line: "+line)
                return None

        result = []
        for tl in table_lines:
            ml=matchBIRDBGPRTLine(tl)
            if(ml):
                # generate table content
                result.append([
252
                        (decorator_helper.decoratePrefix(ml[0]),),
253 254 255 256 257
                        (ml[1],),
                        (ml[2],),
                        (ml[3],),
                        (ml[4],),
                        (ml[5],),
258
                        (self._decorateOriginAS(ml[6],decorator_helper),),
259 260 261 262
                        ])
        return result


263 264 265 266
    def decorateResult(self,session,decorator_helper=None):
        if(not session):
            raise Exception("Can not decorate result without valid session.")

267 268 269
	if(session.getResult() == None):
            return (decorator_helper.pre(defaults.STRING_EMPTY), 1)

270 271
        if((not session.getRouter()) or (not decorator_helper)):
            return "<pre>\n%s\n</pre>" % session.getResult()
272 273 274 275 276 277 278 279 280 281

        table=[]
        table_header=['Prefix',
                      'Next-hop',
                      'Interface',
                      'Since',
                      'Status',
                      'Metric',
                      'Info',]

282
        lines = str.splitlines(session.getResult())
283
        result_len = len(lines)
284 285
        lines = lines[session.getRange():session.getRange()+defaults.range_step]
        table = self._genTable(lines,decorator_helper,session.getRouter())
286

287
        return (ulgmodel.TableDecorator(table,table_header).decorate(),result_len)
288

289 290 291 292 293 294 295 296 297



class BirdShowRouteExportCommand(AbstractBGPPeerSelectCommand,AbstractRouteTableCommand):
    COMMAND_TEXT = 'show route export %s'

class BirdShowRouteCommand(AbstractRouteTableCommand):
    COMMAND_TEXT = 'show route table %s for %s'

298
    def __init__(self,router,name=None):
299
        ulgmodel.TextCommand.__init__(self,self.COMMAND_TEXT,param_specs=[
300
                router.getRoutingTableSelect(),
301 302 303 304
                ulgmodel.TextParameter(pattern=IPV46_SUBNET_REGEXP,name=defaults.STRING_IPSUBNET),
                ],name=name)


305 306 307
class BirdShowRouteProtocolCommand(BirdShowRouteCommand):
    COMMAND_TEXT = 'show route table %s protocol %s'

308 309
    def __init__(self,router,name=None):
        ulgmodel.TextCommand.__init__(self,self.COMMAND_TEXT,param_specs=[router.getRoutingTableSelect(),router.getBGPPeerSelect()],name=name)
310

311

312
class BirdShowRouteAllCommand(ulgmodel.TextCommand):
313 314
    COMMAND_TEXT = 'show route table %s all for %s'

315
    def __init__(self,router,name=None):
316
        ulgmodel.TextCommand.__init__(self,self.COMMAND_TEXT,param_specs=[
317
                router.getRoutingTableSelect(),
318
                ulgmodel.TextParameter(pattern=IPV46_SUBNET_REGEXP,name=defaults.STRING_IPSUBNET),
319 320
                ],
                                      name=name)
321

322 323 324 325 326 327 328 329 330
    def decorateResult(self,session,decorator_helper=None):
        def decorateLine(l):			
            m = bird_sh_route_all_ases_regexp.match(l)
            if(m):
                r = m.group(1)
                ases = str.split(m.group(2))
                for asn in ases:
                    r = r + decorator_helper.decorateASN(asn,prefix='')
                    r = r + ' '
331
                return decorator_helper.annotateIPs(r)
332
            else:
333
                return decorator_helper.annotateIPs(l)
334

335 336
	if(session.getResult() == None):
            return (decorator_helper.pre(defaults.STRING_EMPTY), 1)
337 338 339 340 341 342 343 344 345

        s = str.splitlines(session.getResult())
        r=''
        for sl in s:
            r += decorateLine(sl) + "\n"
            
        return ("<pre>\n%s\n</pre>" % r, len(s))


Tomas Hlavacek's avatar
Tomas Hlavacek committed
346 347 348
class BirdGraphShowRouteAll(ulgmodel.TextCommand):
    COMMAND_TEXT = 'show route table %s all for %s'

349
    def __init__(self,router,name=BIRD_GRAPH_SH_ROUTE_ALL):
Tomas Hlavacek's avatar
Tomas Hlavacek committed
350
        ulgmodel.TextCommand.__init__(self,self.COMMAND_TEXT,param_specs=[
351
                router.getRoutingTableSelect(),
Tomas Hlavacek's avatar
Tomas Hlavacek committed
352 353 354 355
                ulgmodel.TextParameter(pattern=IPV46_SUBNET_REGEXP,name=defaults.STRING_IPSUBNET),
                ],
                                      name=name)

356 357 358
    def finishHook(self,session):
        session.setData(bird_parse_sh_route_all(session.getResult(),str(session.getRouter().getASN())))

Tomas Hlavacek's avatar
Tomas Hlavacek committed
359
    def decorateResult(self,session,decorator_helper=None):
360
        if(session.isFinished()):
361
            if(session.getData() != None) and (session.getData() != []):
362
                return (decorator_helper.img(decorator_helper.getSpecialContentURL(session.getSessionId()),defaults.STRING_BGP_GRAPH),1)
363
            else:
364
                return (decorator_helper.pre(defaults.STRING_BGP_GRAPH_ERROR), 1)
365 366
        else:
            return ('',0)
Tomas Hlavacek's avatar
Tomas Hlavacek committed
367 368

    def getSpecialContent(self,session,**params):
369
        paths = session.getData()
Tomas Hlavacek's avatar
Tomas Hlavacek committed
370 371 372 373 374 375 376
        print "Content-type: image/png\n"
        ulggraph.bgp_graph_gen(bird_reduce_paths(paths),start=session.getRouter().getName(),
			       end=session.getParameters()[1])

    def showRange(self):
        return False

377
class BirdRouter(ulgmodel.Router):
378
    """ Abstract class representing common base for BIRD router objects. """
379
    RESCAN_PEERS_COMMAND = 'show protocols'
380
    RESCAN_TABLES_COMMAND = 'show symbols'
381
    DEFAULT_PROTOCOL_FLTR = '^BGP.*$'
382

383 384 385
    def __init__(self):
        self.bgp_peers = None
        self.routing_tables = None
386
        self.bgp_peer_select = None
387 388
        self.rt_select = None

389
    def _getDefaultCommands(self):
390 391 392
        sh_proto_all = BirdShowProtocolsAllCommand(self)
        sh_proto_route = BirdShowRouteProtocolCommand(self)
        sh_proto_export = BirdShowRouteExportCommand(self)
393
        return [BirdShowProtocolsCommand(show_proto_all_command=sh_proto_all, proto_filter = self.proto_fltr),
394
                BirdShowRouteCommand(self),
395 396 397
                sh_proto_all,
                sh_proto_route,
                sh_proto_export,
398 399
                BirdShowRouteAllCommand(self),
                BirdGraphShowRouteAll(self),
400 401
#                ulgmodel.TextCommand('show status'),
#                ulgmodel.TextCommand('show memory')
402 403
                ]

404 405 406 407 408 409 410 411 412
    def rescanPeers(self):
        res = self.runRawSyncCommand(self.RESCAN_PEERS_COMMAND)
        psp = parseBirdShowProtocols(res)

        peers = []
        for pspl in psp[1]:
            if(re.match(self.proto_fltr,pspl[1])):
                peers.append(pspl[0])

413
        self.bgp_peers = sorted(peers)
414 415 416 417 418 419 420 421 422 423

    def rescanRoutingTables(self):
        res = self.runRawSyncCommand(self.RESCAN_TABLES_COMMAND)

        tables = []
        for l in str.splitlines(res):
            m = bird_show_symbols_line_regexp.match(l)
            if(m and m.group(2).lstrip().rstrip() == STRING_SYMBOL_ROUTING_TABLE):
                tables.append(m.group(1))

424
        self.routing_tables = sorted(tables)
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448

    def getBGPPeers(self):
        if(not self.bgp_peers):
            self.rescanPeers()

        return self.bgp_peers

    def getRoutingTables(self):
        if(not self.routing_tables):
            self.rescanRoutingTables()

        return self.routing_tables

    def initBGPPeerSelect(self,peers):
        rid = hashlib.md5(self.getName()).hexdigest()
        self.bgp_peer_select = ulgmodel.CommonSelectionParameter(rid+"bgp",[tuple((p,p,)) for p in peers],
                                                                  name=defaults.STRING_PEERID)

    def initRoutingTableSelect(self,rt):
        rid = hashlib.md5(self.getName()).hexdigest()
        self.rt_select = ulgmodel.CommonSelectionParameter(rid+"rt",[tuple((p,p,)) for p in rt],
                                                                  name=defaults.STRING_RTABLE)

    def getBGPPeerSelect(self):
449
        if(not self.bgp_peer_select):
450 451 452 453 454 455 456 457 458 459 460 461
            self.initBGPPeerSelect(self.getBGPPeers())

        return self.bgp_peer_select

    def getRoutingTableSelect(self):
        if(not self.rt_select):
            self.initRoutingTableSelect(self.getRoutingTables())

        return self.rt_select



462
class BirdRouterLocal(ulgmodel.LocalRouter,BirdRouter):
463 464
    def __init__(self,sock=defaults.default_bird_sock,commands=None,proto_fltr=None,asn='My ASN',name='localhost',acl=None):
        ulgmodel.LocalRouter.__init__(self,acl=acl)
465 466
        BirdRouter.__init__(self)

467
        self.sock = sock
468
        self.setName(name)
Tomas Hlavacek's avatar
Tomas Hlavacek committed
469
        self.setASN(asn)
470 471 472 473
        if(proto_fltr):
            self.proto_fltr = proto_fltr
        else:
            self.proto_fltr = self.DEFAULT_PROTOCOL_FLTR
474

475 476 477
        self.rescanPeers()
        self.rescanRoutingTables()

478
        # command autoconfiguration might run only after other parameters are set
479 480 481
        if(commands):
            self.setCommands(commands)
        else:
482 483
            self.setCommands(self._getDefaultCommands())

484

485
    def runRawCommand(self,command,outfile):
486 487 488 489 490
        def parseBirdSockLine(line):
            hm = bird_sock_header_regexp.match(line)
            if(hm):
                # first line of the reply
                return (int(hm.group(1)),hm.group(2))
491

492 493 494 495
            em = bird_sock_reply_end_regexp.match(line)
            if(em):
                # most likely the last line of the reply
                return (int(em.group(1)),None)
496

497 498 499
            if(line[0] == '+'):
                # ignore async reply
                return (None,None)
500

501 502 503
            if(line[0] == ' '):
                # return reply line as it is (remove padding)
                return (None,line[1:])
504

505
            raise Exception("Can not parse BIRD output line: "+line)
506

507 508
        def isBirdSockReplyEnd(code):
            if(code==None):
509 510
                return False

511 512 513 514 515
            if(code == 0):
                # end of reply
                return True
            elif(code == 13):
                # show status last line
516
                return True
517 518 519
            elif(code == 8001):
                # network not in table end
                return True
520 521 522
            elif(code >= 9000):
                # probably error
                return True
523 524 525
            else:
                return False

526 527 528 529 530
#        try:
        # open socket to BIRD
        s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        s.settimeout(defaults.default_bird_sock_timeout)
        s.connect(self.sock)
531

532 533
        # cretate FD for the socket
        sf=s.makefile()
534

535 536
        # wait for initial header
        l = sf.readline()
537

538 539 540
        # send the command string
        sf.write(command+"\n")
        sf.flush()
541

542 543 544
        # read and capture lines until the output delimiter string is hit
        while(True):
            l = sf.readline()
545

546
            ulgmodel.debug("Raw line read: " + l)
547

548 549 550 551 552
            # process line according to rules take out from the C code
            lp = parseBirdSockLine(l)
            if(isBirdSockReplyEnd(lp[0])):
                # End of reply (0000 or similar code)
                ulgmodel.debug("End of reply. Code="+str(lp[0]))
553

554 555
                if(lp[1]):
                    ulgmodel.debug("Last read line after normalize: " + lp[1])
556
                    outfile.write(lp[1].rstrip()+"\n")
557 558 559 560
                break
            else:
                if(lp[1]):
                    ulgmodel.debug("Read line after normalize: " + lp[1])
561
                    outfile.write(lp[1].rstrip()+"\n")
562
                else:
563
                    ulgmodel.debug("Read line was empty after normalize.")
564

565 566
        # close the socket and return captured result
        s.close()
567

568 569 570
#        except socket.timeout as e:
#            # catch only timeout exception, while letting other exceptions pass
#            outfile.result(defaults.STRING_SOCKET_TIMEOUT)
571 572 573

    def getForkNeeded(self):
        return False
574 575


576
class BirdRouterRemote(ulgmodel.RemoteRouter,BirdRouter):
577 578 579
    PS_KEY_BGP = '-bgppeers'
    PS_KEY_RT = '-routetab'

580 581
    def __init__(self,host,user,password='',port=22,commands=None,proto_fltr=None,asn='My ASN',name=None,bin_birdc=None,bin_ssh=None,acl=None):
        ulgmodel.RemoteRouter.__init__(self,acl=acl)
582 583
        BirdRouter.__init__(self)

584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
        self.setHost(host)
        self.setUser(user)
        self.setPassword(password)
        self.setPort(port)
        if(name):
            self.setName(name)
        else:
            self.setName(host)
        self.setASN(asn)
        if(proto_fltr):
            self.proto_fltr = proto_fltr
        else:
            self.proto_fltr = self.DEFAULT_PROTOCOL_FLTR
        if(bin_birdc):
            self.bin_birdc = bin_birdc
        else:
            self.bin_birdc = defaults.default_bin_birdc

602
        if(bin_ssh):
603 604 605 606
            self.bin_ssh = bin_ssh
        else:
            self.bin_ssh = defaults.bin_ssh

607 608 609 610 611
        if(defaults.rescan_on_display):
            self.rescanHook()
        else:
            self.loadPersistentInfo()

612 613 614 615 616 617
        # command autoconfiguration might run only after other parameters are set
        if(commands):
            self.setCommands(commands)
        else:
            self.setCommands(self._getDefaultCommands())

618

619 620 621 622
    def getForkNeeded(self):
        return True

    def runRawCommand(self,command,outfile):
623
        c = '/bin/bash -c \'echo "'+command+'" | '+self.bin_ssh+' -p'+str(self.getPort())+' '+str(self.getUser())+'@'+self.getHost()+' '+self.bin_birdc+'\''
624
        skiplines = 2
625
        s=pexpect.spawn(c,timeout=defaults.timeout)
626

627
#        s.logfile = open('/tmp/ulgbird.log', 'w')
628

629 630 631
        # handle ssh
        y=0
        p=0
632 633
        l=0
        capture=False
634
        while True:
635
            i=s.expect([STRING_EXPECT_SSH_NEWKEY,STRING_EXPECT_PASSWORD,STRING_EXPECT_REPLY_START,'\n',pexpect.EOF,pexpect.TIMEOUT])
636 637 638 639
            if(i==0):
                if(y>1):
                    raise Exception("pexpect session failed: Can not save SSH key.")

640
                s.sendline('yes')
641 642 643 644 645
                y+=1
            elif(i==1):
                if(p>1):
                    raise Exception("pexpect session failed: Password not accepted.")

646
                s.sendline(self.password)
647
                p+=1
648 649
            elif(i==2):
                capture=True
650
            elif(i==3):
651 652 653 654 655 656 657
                if(capture):
                    if(l>=skiplines):
                        outfile.write(re.sub(BIRD_CONSOLE_PROMPT_REGEXP,'',s.before))
                    l+=1
            elif(i==4): # EOF -> process output
                break
            elif(i==5):
658
                raise Exception("pexpect session timed out. last output: "+s.before)
659
            else:
660
                raise Exception("pexpect session failed: Unknown error. last output: "+s.before)
661 662


663
    def savePersistentInfo(self):
664 665
        key_bgp = self.getHost() + self.getName() + self.PS_KEY_BGP
        key_rt = self.getHost() + self.getName() + self.PS_KEY_RT
666 667

        
668
        ps = ulgmodel.loadPersistentStorage()
669 670 671 672 673
        ps.set(key_bgp,self.getBGPPeers())
        ps.set(key_rt,self.getRoutingTables())
        ps.save()
               
    def loadPersistentInfo(self):
674 675
        key_bgp = self.getHost() + self.getName() + self.PS_KEY_BGP
        key_rt = self.getHost() + self.getName() + self.PS_KEY_RT
676

677
        ps = ulgmodel.loadPersistentStorage()
678 679 680 681 682 683 684 685 686 687 688
        self.bgp_peers = ps.get(key_bgp)
        self.routing_tables = ps.get(key_rt)

        if(not self.getBGPPeers()) or (not self.getRoutingTables()):
            self.rescanHook()


    def rescanHook(self):
        self.rescanPeers()
        self.rescanRoutingTables()
        self.savePersistentInfo()