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,resrange=None):
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 142 143 144
    if(resrange):
        return (header,table[resrange:resrange+defaults.range_step],len(table))
    else:
        return (header,table,len(table))
145 146 147 148 149 150

# classes

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

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

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

197 198 199
	if(session.getResult() == None):
            return (decorator_helper.pre(defaults.STRING_EMPTY), 1)

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

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

213
            return (ulgmodel.TableDecorator(table,table_header).decorate(),pr[2])
214 215


216
class AbstractBGPPeerSelectCommand(ulgmodel.TextCommand):
217
    """ Abstract class for all BIRD BGP peer-specific commands """
218

219 220
    def __init__(self,router,name=None):
        ulgmodel.TextCommand.__init__(self,self.COMMAND_TEXT,param_specs=[router.getBGPPeerSelect()],name=name)
221

222 223

class BirdShowProtocolsAllCommand(AbstractBGPPeerSelectCommand):
224 225 226
    COMMAND_TEXT = 'show protocols all %s'


227

228 229


230
class AbstractRouteTableCommand(ulgmodel.TextCommand):
231

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

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


266 267 268 269
    def decorateResult(self,session,decorator_helper=None):
        if(not session):
            raise Exception("Can not decorate result without valid session.")

270 271 272
	if(session.getResult() == None):
            return (decorator_helper.pre(defaults.STRING_EMPTY), 1)

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

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

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

290
        return (ulgmodel.TableDecorator(table,table_header).decorate(),result_len)
291

292 293 294 295 296 297 298 299 300



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

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

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


308 309 310
class BirdShowRouteProtocolCommand(BirdShowRouteCommand):
    COMMAND_TEXT = 'show route table %s protocol %s'

311 312
    def __init__(self,router,name=None):
        ulgmodel.TextCommand.__init__(self,self.COMMAND_TEXT,param_specs=[router.getRoutingTableSelect(),router.getBGPPeerSelect()],name=name)
313

314

315
class BirdShowRouteAllCommand(ulgmodel.TextCommand):
316 317
    COMMAND_TEXT = 'show route table %s all for %s'

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

325 326 327 328 329 330 331 332 333
    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 + ' '
334
                return decorator_helper.annotateIPs(r)
335
            else:
336
                return decorator_helper.annotateIPs(l)
337

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

        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
349 350 351
class BirdGraphShowRouteAll(ulgmodel.TextCommand):
    COMMAND_TEXT = 'show route table %s all for %s'

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

359 360 361
    def finishHook(self,session):
        session.setData(bird_parse_sh_route_all(session.getResult(),str(session.getRouter().getASN())))

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

    def getSpecialContent(self,session,**params):
372
        paths = session.getData()
Tomas Hlavacek's avatar
Tomas Hlavacek committed
373 374 375 376 377 378 379
        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

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

386 387 388
    def __init__(self):
        self.bgp_peers = None
        self.routing_tables = None
389
        self.bgp_peer_select = None
390 391
        self.rt_select = None

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

407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
    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])

        self.bgp_peers = peers

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

        self.routing_tables = tables

    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):
452
        if(not self.bgp_peer_select):
453 454 455 456 457 458 459 460 461 462 463 464
            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



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

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

478 479 480
        self.rescanPeers()
        self.rescanRoutingTables()

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

487

488
    def runRawCommand(self,command,outfile):
489 490 491 492 493
        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))
494

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

500 501 502
            if(line[0] == '+'):
                # ignore async reply
                return (None,None)
503

504 505 506
            if(line[0] == ' '):
                # return reply line as it is (remove padding)
                return (None,line[1:])
507

508
            raise Exception("Can not parse BIRD output line: "+line)
509

510 511
        def isBirdSockReplyEnd(code):
            if(code==None):
512 513
                return False

514 515 516 517 518
            if(code == 0):
                # end of reply
                return True
            elif(code == 13):
                # show status last line
519
                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
    def __init__(self,host,user,password='',port=22,commands=None,proto_fltr=None,asn='My ASN',name=None,bin_birdc=None,bin_ssh=None):
581
        ulgmodel.RemoteRouter.__init__(self)
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 603 604 605 606
        if(bin_birdc):
            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 618 619 620 621
        # 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):
622
        c = '/bin/bash -c \'echo "'+command+'" | '+self.bin_ssh+' -p'+str(self.getPort())+' '+str(self.getUser())+'@'+self.getHost()+' '+self.bin_birdc+'\''
623
        skiplines = 2
624
        s=pexpect.spawn(c,timeout=defaults.timeout)
625

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

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

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

645
                s.sendline(self.password)
646
                p+=1
647 648
            elif(i==2):
                capture=True
649
            elif(i==3):
650 651 652 653 654 655 656
                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):
657
                raise Exception("pexpect session timed out. last output: "+s.before)
658
            else:
659
                raise Exception("pexpect session failed: Unknown error. last output: "+s.before)
660 661


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

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

676
        ps = ulgmodel.loadPersistentStorage()
677 678 679 680 681 682 683 684 685 686 687
        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()