ulgbird.py 22.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#!/usr/bin/env python
#
# ULG - Universal Looking Glass
# by Tomas Hlavacek (tomas.hlavacek@nic.cz)
# last udate: June 21 2012
#
# 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
25
import pexpect
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 39 40
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\._-]+>'
STRING_LOGOUT_COMMAND = 'exit'

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

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


47 48
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*$'
49

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

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

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

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

63
BIRD_SH_ROUTE_ALL_ASES_REGEXP = "^(\s*BGP\.as_path:\s+)([0-9\s]+)\s*$"
Tomas Hlavacek's avatar
Tomas Hlavacek committed
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
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 = []
79
    params = dict(DEFAULT_PARAMS)
Tomas Hlavacek's avatar
Tomas Hlavacek committed
80 81 82 83 84 85 86 87 88
    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):
89
            ases = [ulgmodel.annotateAS("AS"+str(asn)) for asn in [prependas] + split_ases(m.group(2))]
Tomas Hlavacek's avatar
Tomas Hlavacek committed
90
            res.append((ases,params))
91
            params = dict(DEFAULT_PARAMS)
Tomas Hlavacek's avatar
Tomas Hlavacek committed
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
            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)

107
def parseBirdShowProtocols(text,resrange=None):
108 109 110 111 112 113 114
    def parseShowProtocolsLine(line):
        sh_proto_line_regexp = re.compile(BIRD_SHOW_PROTO_LINE_REGEXP)
        m = sh_proto_line_regexp.match(line)
        if(m):
            res = list(m.groups()[0:5])
            if(m.group(6)):
                res.append(m.group(6))
115

116 117
            return res
        else:
118
            ulgmodel.log("WARN: bird.parseShowProtocolsLine failed to match line: "+line)
119
            return None
120

121 122 123

    header = []
    table = []
124

125 126 127 128 129 130 131 132 133 134 135 136
    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)
            else:
137
                ulgmodel.log("ulgbird.parseBirdShowProtocols skipping unparsable line: "+l)
138

139 140 141 142
    if(resrange):
        return (header,table[resrange:resrange+defaults.range_step],len(table))
    else:
        return (header,table,len(table))
143 144 145 146 147 148

# classes

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

149
    def __init__(self,name=None,show_proto_all_command=None,proto_filter=None):
150
        ulgmodel.TextCommand.__init__(self,self.COMMAND_TEXT,param_specs=[],name=name)
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
        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
168

169
    def _decorateTableLine(self,table_line,router,decorator_helper):
170 171 172 173 174 175 176 177 178
        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])
179 180 181 182 183 184 185 186 187 188
        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
189

190

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

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

202
            for tl in pr[1][session.getRange():session.getRange()+defaults.range_step]:
203 204 205
                # 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
206
                table.append(self._decorateTableLine(tl,session.getRouter(),decorator_helper))
207

208
            return (ulgmodel.TableDecorator(table,table_header).decorate(),pr[2])
209 210


211 212
class BirdBGPPeerSelectCommand(ulgmodel.TextCommand):
    """ Abstract class for all BIRD BGP peer-specific commands """
213 214 215 216 217 218

    def __init__(self,peers,name=None):
        peer_param = ulgmodel.SelectionParameter([tuple((p,p,)) for p in peers],
                                                 name=defaults.STRING_PEERID)
        ulgmodel.TextCommand.__init__(self,self.COMMAND_TEXT,param_specs=[peer_param],name=name)

219 220 221 222 223 224
class BirdShowProtocolsAllCommand(BirdBGPPeerSelectCommand):
    COMMAND_TEXT = 'show protocols all %s'

class BirdShowRouteExportCommand(BirdBGPPeerSelectCommand):
    COMMAND_TEXT = 'show route export %s'

225
class BirdShowRouteCommand(ulgmodel.TextCommand):
226 227 228 229 230
    COMMAND_TEXT = 'show route table %s for %s'

    def __init__(self,tables,name=None):
        table_param = ulgmodel.SelectionParameter([tuple((t,t,)) for t in tables],
                                                  name=defaults.STRING_RTABLE)
231 232

        ulgmodel.TextCommand.__init__(self,self.COMMAND_TEXT,param_specs=[
233 234
                table_param,
                ulgmodel.TextParameter(pattern=IPV46_SUBNET_REGEXP,name=defaults.STRING_IPSUBNET),
235 236
                ],name=name)

237

238 239 240 241
    def _decorateOriginAS(self,asfield,decorator_helper):
        # expected input is "[AS28171i]"
        m = bird_asfield_regexp.match(asfield)
        if(m):
242
            return '['+decorator_helper.decorateASN(m.group(1))+m.group(2)+']'
243 244 245
        else:
            return asfield

246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
    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([
261
                        (decorator_helper.decoratePrefix(ml[0]),),
262 263 264 265 266
                        (ml[1],),
                        (ml[2],),
                        (ml[3],),
                        (ml[4],),
                        (ml[5],),
267
                        (self._decorateOriginAS(ml[6],decorator_helper),),
268 269 270 271
                        ])
        return result


272 273 274 275 276 277
    def decorateResult(self,session,decorator_helper=None):
        if(not session):
            raise Exception("Can not decorate result without valid session.")

        if((not session.getRouter()) or (not decorator_helper)):
            return "<pre>\n%s\n</pre>" % session.getResult()
278 279 280 281 282 283 284 285 286 287

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

288
        lines = str.splitlines(session.getResult())
289
        result_len = len(lines)
290 291
        lines = lines[session.getRange():session.getRange()+defaults.range_step]
        table = self._genTable(lines,decorator_helper,session.getRouter())
292

293
        return (ulgmodel.TableDecorator(table,table_header).decorate(),result_len)
294

295 296 297 298 299 300 301 302 303 304
class BirdShowRouteProtocolCommand(BirdShowRouteCommand):
    COMMAND_TEXT = 'show route table %s protocol %s'

    def __init__(self,peers,tables,name=None):
        peer_param = ulgmodel.SelectionParameter([tuple((p,p,)) for p in peers],
                                                 name=defaults.STRING_PEERID)
        table_param = ulgmodel.SelectionParameter([tuple((t,t,)) for t in tables],
                                                  name=defaults.STRING_RTABLE)

        ulgmodel.TextCommand.__init__(self,self.COMMAND_TEXT,param_specs=[table_param, peer_param],name=name)
305

306

307
class BirdShowRouteAllCommand(ulgmodel.TextCommand):
308 309 310 311 312
    COMMAND_TEXT = 'show route table %s all for %s'

    def __init__(self,tables,name=None):
        table_param = ulgmodel.SelectionParameter([tuple((t,t,)) for t in tables],
                                                  name=defaults.STRING_RTABLE)
313

314
        ulgmodel.TextCommand.__init__(self,self.COMMAND_TEXT,param_specs=[
315 316
                table_param,
                ulgmodel.TextParameter(pattern=IPV46_SUBNET_REGEXP,name=defaults.STRING_IPSUBNET),
317 318
                ],
                                      name=name)
319

320 321 322 323 324 325 326 327 328
    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 + ' '
329
                return decorator_helper.annotateIPs(r)
330
            else:
331
                return decorator_helper.annotateIPs(l)
332 333 334 335 336 337 338 339 340 341


        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
342 343 344 345 346 347 348 349 350 351 352 353 354
class BirdGraphShowRouteAll(ulgmodel.TextCommand):
    COMMAND_TEXT = 'show route table %s all for %s'

    def __init__(self,tables,name=BIRD_GRAPH_SH_ROUTE_ALL):
        table_param = ulgmodel.SelectionParameter([tuple((t,t,)) for t in tables],
                                                  name=defaults.STRING_RTABLE)

        ulgmodel.TextCommand.__init__(self,self.COMMAND_TEXT,param_specs=[
                table_param,
                ulgmodel.TextParameter(pattern=IPV46_SUBNET_REGEXP,name=defaults.STRING_IPSUBNET),
                ],
                                      name=name)

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

Tomas Hlavacek's avatar
Tomas Hlavacek committed
358
    def decorateResult(self,session,decorator_helper=None):
359 360 361 362
        if(session.isFinished()):
            return (decorator_helper.img(decorator_helper.getSpecialContentURL(session.getSessionId()),"BGP graph"),1)
        else:
            return ('',0)
Tomas Hlavacek's avatar
Tomas Hlavacek committed
363 364

    def getSpecialContent(self,session,**params):
365
        paths = session.getData()
Tomas Hlavacek's avatar
Tomas Hlavacek committed
366 367 368 369 370 371 372
        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

373
class BirdRouter(ulgmodel.Router):
374
    RESCAN_PEERS_COMMAND = 'show protocols'
375
    RESCAN_TABLES_COMMAND = 'show symbols'
376
    DEFAULT_PROTOCOL_FLTR = '^(Kernel|Device|Static|BGP).*$'
377

378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
    def _getDefaultCommands(self):
        sh_proto_all = BirdShowProtocolsAllCommand(self.getBGPPeers())
        sh_proto_route = BirdShowRouteProtocolCommand(self.getBGPPeers(),self.getRoutingTables())
        sh_proto_export = BirdShowRouteExportCommand(self.getBGPPeers())
        return [BirdShowProtocolsCommand(show_proto_all_command=sh_proto_all, proto_filter = self.proto_fltr),
                BirdShowRouteCommand(self.getRoutingTables()),
                sh_proto_all,
                sh_proto_route,
                sh_proto_export,
                BirdShowRouteAllCommand(self.getRoutingTables()),
                BirdGraphShowRouteAll(self.getRoutingTables()),
                ulgmodel.TextCommand('show status'),
                ulgmodel.TextCommand('show memory')
                ]

class BirdRouterLocal(ulgmodel.LocalRouter,BirdRouter):
394
    def __init__(self,sock=defaults.default_bird_sock,commands=None,proto_fltr=None,asn='My ASN',name='localhost'):
395
        ulgmodel.LocalRouter.__init__(self)
396
        self.sock = sock
397
        self.setName(name)
Tomas Hlavacek's avatar
Tomas Hlavacek committed
398
        self.setASN(asn)
399 400 401 402
        if(proto_fltr):
            self.proto_fltr = proto_fltr
        else:
            self.proto_fltr = self.DEFAULT_PROTOCOL_FLTR
403

404
        # command autoconfiguration might run only after other parameters are set
405 406 407
        if(commands):
            self.setCommands(commands)
        else:
408 409
            self.setCommands(self._getDefaultCommands())

410

411
    def runRawCommand(self,command,outfile):
412 413 414 415 416
        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))
417

418 419 420 421
            em = bird_sock_reply_end_regexp.match(line)
            if(em):
                # most likely the last line of the reply
                return (int(em.group(1)),None)
422

423 424 425
            if(line[0] == '+'):
                # ignore async reply
                return (None,None)
426

427 428 429
            if(line[0] == ' '):
                # return reply line as it is (remove padding)
                return (None,line[1:])
430

431
            raise Exception("Can not parse BIRD output line: "+line)
432

433 434
        def isBirdSockReplyEnd(code):
            if(code==None):
435 436
                return False

437 438 439 440 441
            if(code == 0):
                # end of reply
                return True
            elif(code == 13):
                # show status last line
442
                return True
443 444 445
            elif(code >= 9000):
                # probably error
                return True
446 447 448
            else:
                return False

449 450 451 452 453
#        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)
454

455 456
        # cretate FD for the socket
        sf=s.makefile()
457

458 459
        # wait for initial header
        l = sf.readline()
460

461 462 463
        # send the command string
        sf.write(command+"\n")
        sf.flush()
464

465 466 467
        # read and capture lines until the output delimiter string is hit
        while(True):
            l = sf.readline()
468

469
            ulgmodel.debug("Raw line read: " + l)
470

471 472 473 474 475
            # 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]))
476

477 478
                if(lp[1]):
                    ulgmodel.debug("Last read line after normalize: " + lp[1])
479
                    outfile.write(lp[1].rstrip()+"\n")
480 481 482 483
                break
            else:
                if(lp[1]):
                    ulgmodel.debug("Read line after normalize: " + lp[1])
484
                    outfile.write(lp[1].rstrip()+"\n")
485
                else:
486
                    ulgmodel.debug("Read line was empty after normalize.")
487

488 489
        # close the socket and return captured result
        s.close()
490

491 492 493
#        except socket.timeout as e:
#            # catch only timeout exception, while letting other exceptions pass
#            outfile.result(defaults.STRING_SOCKET_TIMEOUT)
494 495 496

    def getForkNeeded(self):
        return False
497 498


499
    def rescanPeers(self):
500
        res = self.runRawSyncCommand(self.RESCAN_PEERS_COMMAND)
501
        psp = parseBirdShowProtocols(res)
502 503

        peers = []
504
        for pspl in psp[1]:
505
            if(re.match(self.proto_fltr,pspl[1])):
506
                peers.append(pspl[0])
507 508 509

        return peers

510 511 512 513 514 515 516 517
    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))
Tomas Hlavacek's avatar
Tomas Hlavacek committed
518

519
        return tables
520

521
    def getBGPPeers(self):
522
        return self.rescanPeers()
523

524 525
    def getRoutingTables(self):
        return self.rescanRoutingTables()
526 527 528


class BirdRouterRemote(ulgmodel.RemoteRouter,BirdRouter):
529 530 531
    PS_KEY_BGP = '-bgppeers'
    PS_KEY_RT = '-routetab'

532
    def __init__(self,host,user,password='',port=22,commands=None,proto_fltr=None,asn='My ASN',name=None,bin_birdc=None,bin_ssh=None):
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
        ulgmodel.RemoteRouter.__init__(self)
        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

552 553 554 555 556
        if(bin_birdc):
            self.bin_ssh = bin_ssh
        else:
            self.bin_ssh = defaults.bin_ssh

557 558 559 560 561
        if(defaults.rescan_on_display):
            self.rescanHook()
        else:
            self.loadPersistentInfo()

562 563 564 565 566 567 568 569 570 571
        # command autoconfiguration might run only after other parameters are set
        if(commands):
            self.setCommands(commands)
        else:
            self.setCommands(self._getDefaultCommands())

    def getForkNeeded(self):
        return True

    def runRawCommand(self,command,outfile):
572
        c = '/bin/bash -c \'echo "'+command+'" | '+self.bin_ssh+' -p'+str(self.getPort())+' '+str(self.getUser())+'@'+self.getHost()+' '+self.bin_birdc+'\''
573
        s=pexpect.spawn(c,timeout=defaults.timeout)
574

575
#        s.logfile = open('/tmp/ulgbird.log', 'w')
576

577 578 579 580
        # handle ssh
        y=0
        p=0
        while True:
581
            i=s.expect([STRING_EXPECT_SSH_NEWKEY,STRING_EXPECT_PASSWORD,pexpect.EOF,pexpect.TIMEOUT])
582 583 584 585
            if(i==0):
                if(y>1):
                    raise Exception("pexpect session failed: Can not save SSH key.")

586
                s.sendline('yes')
587 588 589 590 591
                y+=1
            elif(i==1):
                if(p>1):
                    raise Exception("pexpect session failed: Password not accepted.")

592
                s.sendline(self.password)
593 594 595
                p+=1
            elif(i==2): # EOF -> process output
                break
596 597
            elif(i==3):
                raise Exception("pexpect session timed out. last output: "+s.before)
598
            else:
599
                raise Exception("pexpect session failed: Unknown error. last output: "+s.before)
600 601


602
        def stripFirstLines(string):
603
            lines = str.splitlines(string)
604 605
            r = re.sub(BIRD_CONSOLE_PROMPT_REGEXP,'',lines[2]) + '\n'
            for l in lines[3:]:
606 607 608
                r = r + l + '\n'
            return r

609
        out = s.before
610
#        ulgmodel.debug("BIRD OUT: "+out)
611
        outfile.write(stripFirstLines(out))
612 613 614 615 616 617 618 619 620 621

    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])

622
        self.bgp_peers = peers
623 624 625 626 627 628 629 630 631 632

    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))

633
        self.routing_tables = tables
634 635

    def getBGPPeers(self):
636
        return self.bgp_peers
637 638

    def getRoutingTables(self):
639 640 641
        return self.routing_tables

    def savePersistentInfo(self):
642 643
        key_bgp = self.getHost() + self.getName() + self.PS_KEY_BGP
        key_rt = self.getHost() + self.getName() + self.PS_KEY_RT
644 645 646 647 648 649 650 651

        
        ps = ulgmodel.PersistentStorage.load()
        ps.set(key_bgp,self.getBGPPeers())
        ps.set(key_rt,self.getRoutingTables())
        ps.save()
               
    def loadPersistentInfo(self):
652 653
        key_bgp = self.getHost() + self.getName() + self.PS_KEY_BGP
        key_rt = self.getHost() + self.getName() + self.PS_KEY_RT
654 655 656 657 658 659 660 661 662 663 664 665 666

        ps = ulgmodel.PersistentStorage.load()
        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()