ulgbird.py 23.1 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 26 27 28

import defaults

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

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

35 36 37
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\._-]+>'
38
STRING_EXPECT_REPLY_START = 'BIRD\s+[^\s]+\s+ready\.'
39 40
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
bird_show_proto_line_regexp = re.compile(BIRD_SHOW_PROTO_LINE_REGEXP)
63

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

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

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

122 123 124

    header = []
    table = []
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)
137 138
#            else:
#                ulgmodel.log("ulgbird.parseBirdShowProtocols skipping unparsable line: "+l)
139

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

# classes

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

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

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

191

192 193 194 195 196 197
    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()
198
        else:
199
            pr = parseBirdShowProtocols(session.getResult(),session.getRange())
200
            table_header = pr[0]
201 202
            table = []

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

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


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

    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)

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

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

226
class BirdShowRouteCommand(ulgmodel.TextCommand):
227 228 229 230 231
    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)
232 233

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

238

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

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


273 274 275 276 277 278
    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()
279 280 281 282 283 284 285 286 287 288

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

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

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

296 297 298 299 300 301 302 303 304 305
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)
306

307

308
class BirdShowRouteAllCommand(ulgmodel.TextCommand):
309 310 311 312 313
    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)
314

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

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


        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
343 344 345 346 347 348 349 350 351 352 353 354 355
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)

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
    RESCAN_PEERS_COMMAND = 'show protocols'
379
    RESCAN_TABLES_COMMAND = 'show symbols'
380
    DEFAULT_PROTOCOL_FLTR = '^BGP.*$'
381

382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
    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):
398
    def __init__(self,sock=defaults.default_bird_sock,commands=None,proto_fltr=None,asn='My ASN',name='localhost'):
399
        ulgmodel.LocalRouter.__init__(self)
400
        self.sock = sock
401
        self.setName(name)
Tomas Hlavacek's avatar
Tomas Hlavacek committed
402
        self.setASN(asn)
403 404 405 406
        if(proto_fltr):
            self.proto_fltr = proto_fltr
        else:
            self.proto_fltr = self.DEFAULT_PROTOCOL_FLTR
407

408
        # command autoconfiguration might run only after other parameters are set
409 410 411
        if(commands):
            self.setCommands(commands)
        else:
412 413
            self.setCommands(self._getDefaultCommands())

414

415
    def runRawCommand(self,command,outfile):
416 417 418 419 420
        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))
421

422 423 424 425
            em = bird_sock_reply_end_regexp.match(line)
            if(em):
                # most likely the last line of the reply
                return (int(em.group(1)),None)
426

427 428 429
            if(line[0] == '+'):
                # ignore async reply
                return (None,None)
430

431 432 433
            if(line[0] == ' '):
                # return reply line as it is (remove padding)
                return (None,line[1:])
434

435
            raise Exception("Can not parse BIRD output line: "+line)
436

437 438
        def isBirdSockReplyEnd(code):
            if(code==None):
439 440
                return False

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

453 454 455 456 457
#        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)
458

459 460
        # cretate FD for the socket
        sf=s.makefile()
461

462 463
        # wait for initial header
        l = sf.readline()
464

465 466 467
        # send the command string
        sf.write(command+"\n")
        sf.flush()
468

469 470 471
        # read and capture lines until the output delimiter string is hit
        while(True):
            l = sf.readline()
472

473
            ulgmodel.debug("Raw line read: " + l)
474

475 476 477 478 479
            # 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]))
480

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

492 493
        # close the socket and return captured result
        s.close()
494

495 496 497
#        except socket.timeout as e:
#            # catch only timeout exception, while letting other exceptions pass
#            outfile.result(defaults.STRING_SOCKET_TIMEOUT)
498 499 500

    def getForkNeeded(self):
        return False
501 502


503
    def rescanPeers(self):
504
        res = self.runRawSyncCommand(self.RESCAN_PEERS_COMMAND)
505
        psp = parseBirdShowProtocols(res)
506 507

        peers = []
508
        for pspl in psp[1]:
509
            if(re.match(self.proto_fltr,pspl[1])):
510
                peers.append(pspl[0])
511 512 513

        return peers

514 515 516 517 518 519 520 521
    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
522

523
        return tables
524

525
    def getBGPPeers(self):
526
        return self.rescanPeers()
527

528 529
    def getRoutingTables(self):
        return self.rescanRoutingTables()
530 531 532


class BirdRouterRemote(ulgmodel.RemoteRouter,BirdRouter):
533 534 535
    PS_KEY_BGP = '-bgppeers'
    PS_KEY_RT = '-routetab'

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

556 557 558 559 560
        if(bin_birdc):
            self.bin_ssh = bin_ssh
        else:
            self.bin_ssh = defaults.bin_ssh

561 562 563 564 565
        if(defaults.rescan_on_display):
            self.rescanHook()
        else:
            self.loadPersistentInfo()

566 567 568 569 570 571 572 573 574 575
        # 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):
576
        c = '/bin/bash -c \'echo "'+command+'" | '+self.bin_ssh+' -p'+str(self.getPort())+' '+str(self.getUser())+'@'+self.getHost()+' '+self.bin_birdc+'\''
577
        skiplines = 2
578
        s=pexpect.spawn(c,timeout=defaults.timeout)
579

580
#        s.logfile = open('/tmp/ulgbird.log', 'w')
581

582 583 584
        # handle ssh
        y=0
        p=0
585 586
        l=0
        capture=False
587
        while True:
588
            i=s.expect([STRING_EXPECT_SSH_NEWKEY,STRING_EXPECT_PASSWORD,STRING_EXPECT_REPLY_START,'\n',pexpect.EOF,pexpect.TIMEOUT])
589 590 591 592
            if(i==0):
                if(y>1):
                    raise Exception("pexpect session failed: Can not save SSH key.")

593
                s.sendline('yes')
594 595 596 597 598
                y+=1
            elif(i==1):
                if(p>1):
                    raise Exception("pexpect session failed: Password not accepted.")

599
                s.sendline(self.password)
600
                p+=1
601 602
            elif(i==2):
                capture=True
603
            elif(i==3):
604 605 606 607 608 609 610
                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):
611
                raise Exception("pexpect session timed out. last output: "+s.before)
612
            else:
613
                raise Exception("pexpect session failed: Unknown error. last output: "+s.before)
614 615 616 617 618 619 620 621 622 623 624


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

625
        self.bgp_peers = peers
626 627 628 629 630 631 632 633 634 635

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

636
        self.routing_tables = tables
637 638

    def getBGPPeers(self):
639
        return self.bgp_peers
640 641

    def getRoutingTables(self):
642 643 644
        return self.routing_tables

    def savePersistentInfo(self):
645 646
        key_bgp = self.getHost() + self.getName() + self.PS_KEY_BGP
        key_rt = self.getHost() + self.getName() + self.PS_KEY_RT
647 648 649 650 651 652 653 654

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

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