Skip to content

Commit

Permalink
Merge pull request #115 from chenailin11/master
Browse files Browse the repository at this point in the history
add api about binary update, and add extend admin group in ls
  • Loading branch information
zlpqingmei committed Sep 29, 2020
2 parents 643f016 + 6714872 commit 99ccef9
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 6 deletions.
32 changes: 31 additions & 1 deletion yabgp/api/utils.py
Expand Up @@ -37,6 +37,7 @@ def decorated_function(*args, **kwargs):
LOG.info('API POST data %s', request.json)
LOG.debug('API request environ %s', request.environ)
return f(*args, **kwargs)

return decorated_function


Expand All @@ -51,6 +52,7 @@ def decorator(*args, **kwargs):
'status': False,
'code': "Please check the peer's state"
})

return decorator


Expand Down Expand Up @@ -151,7 +153,35 @@ def send_update(peer_ip, attr, nlri, withdraw):
:return:
"""
if cfg.CONF.bgp.running_config['factory'].fsm.protocol.send_update({
'attr': attr, 'nlri': nlri, 'withdraw': withdraw}):
'attr': attr, 'nlri': nlri, 'withdraw': withdraw}):
return {
'status': True
}
else:
return {
'status': False,
'code': 'failed when send this message out'
}


def construct_update_to_bin(peer_ip, attr, nlri, withdraw):
"""
send update message to bin
:param peer_ip: peer ip address
:return:
"""
massage_bin = cfg.CONF.bgp.running_config['factory'].fsm.protocol.construct_update_to_bin({
'attr': attr, 'nlri': nlri, 'withdraw': withdraw})
return massage_bin


def send_bin_update(peer_ip, massage):
"""
send bin update message
:param peer_ip: peer ip address
:return:
"""
if cfg.CONF.bgp.running_config['factory'].fsm.protocol.send_bin_update(massage):
return {
'status': True
}
Expand Down
157 changes: 157 additions & 0 deletions yabgp/api/v1.py
Expand Up @@ -15,8 +15,10 @@

"""Blueprint for version 1 of API
"""
import binascii
import logging
import time
import re

from flask_httpauth import HTTPBasicAuth
from flask import Blueprint, request
Expand Down Expand Up @@ -269,3 +271,158 @@ def search_adj_rib_out(peer_ip):
prefix_list = json_request.get('data')
afi_safi = dict(request.args.items()).get('afi_safi') or 'ipv4'
return flask.jsonify(api_utils.get_adj_rib_out(prefix_list, afi_safi))


@blueprint.route('/peer/<peer_ip>/json_to_bin', methods=['POST'])
@auth.login_required
@api_utils.log_request
@api_utils.makesure_peer_establish
def json_to_bin(peer_ip):
json_request = flask.request.get_json()
attr = json_request.get('attr') or {}
nlri = json_request.get('nlri') or []
withdraw = json_request.get('withdraw') or []
format = flask.request.args.get('format') or None
if attr:
attr = {int(k): v for k, v in attr.items()}
res = api_utils.get_peer_conf_and_state(peer_ip)
if 5 not in attr and res['peer']['remote_as'] == res['peer']['local_as']:
# default local preference
attr[5] = 100
if 16 in attr:
# extended community recombine
ext_community = []
for ext_com in attr[16]:
key, value = ext_com.split(':', 1)
if key.strip().lower() == 'route-target':
values = value.strip().split(',')
for vau in values:
if '.' in vau.strip().split(':')[0]:
ext_community.append([258, vau.strip()])
else:
nums = vau.strip().split(':', 1)
# check(2:2)whether the last 2(AN) < 65535
if int(nums[1].strip()) <= 65535:
ext_community.append([2, vau.strip()])
else:
# 4 byte, need to check whether four_bytes_as is true in capability
if res['peer']['capability']['remote']:
four_bytes_as = res['peer']['capability']['remote']['four_bytes_as']
else:
return flask.jsonify({
'status': False,
'code': 'please check peer state'
})
if int(nums[1].strip()) > 65535 and four_bytes_as:
ext_community.append([514, vau.strip()])
elif not four_bytes_as and int(nums[0].strip()) > 65535:
return flask.jsonify({
'status': False,
'code': 'peer not support as num of greater than 65535'
})
elif key.strip().lower() == 'dmzlink-bw':
values = value.strip().split(',')
for vau in values:
ext_community.append([16388, vau.strip()])
elif key.strip().lower() == 'route-origin':
values = value.strip().split(',')
for vau in values:
if '.' in vau.strip().split(':')[0]:
ext_community.append([259, vau.strip()])
else:
if res['peer']['capability']['remote']:
four_bytes_as = res['peer']['capability']['remote']['four_bytes_as']
else:
return flask.jsonify({
'status': False,
'code': 'please check peer state'
})
nums = vau.strip().split(':', 1)
if int(nums[1].strip()) <= 65535 and four_bytes_as:
ext_community.append([515, vau.strip()])
elif not four_bytes_as and int(nums[0].strip()) > 65535:
return flask.jsonify({
'status': False,
'code': 'peer not support as num of greater than 65535'
})
else:
ext_community.append([3, vau.strip()])
elif key.strip().lower() == 'redirect-nexthop':
ext_community.append([2048, '0.0.0.0', int(value.strip())])
elif bgp_cons.BGP_EXT_COM_DICT_1.get(key.strip().lower()):
key_num = bgp_cons.BGP_EXT_COM_DICT_1.get(key.strip().lower())
values = value.strip().split(':', 1)
ext_community.append([key_num, int(values[0]), int(values[1])])
else:
key_num = bgp_cons.BGP_EXT_COM_DICT.get(key.strip().lower())
if key_num:
values = value.strip().split(',')
for vau in values:
if key_num == 32777:
ext_community.append([key_num, int(vau.strip())])
else:
ext_community.append([key_num, vau.strip()])
else:
return flask.jsonify({
'status': False,
'code': 'unexpected extended community "%s", please check your post data' % key
})
attr[16] = ext_community
if (attr and nlri) or withdraw:
massage_bin = api_utils.construct_update_to_bin(peer_ip, attr, nlri, withdraw)
massage = binascii.b2a_hex(massage_bin).decode('utf-8')
if format == 'human':
massage_tmp = ' '.join(re.findall('.{2}', massage))
massage = re.findall('.{24}', massage_tmp)
massage.append(massage_tmp[(len(massage) * 24):])
return flask.jsonify({'bin': massage})
elif 14 in attr or 15 in attr:
massage_bin = api_utils.construct_update_to_bin(peer_ip, attr, nlri, withdraw)
LOG.info('massage_bin:' + binascii.b2a_hex(massage_bin).decode('utf-8'))
massage = binascii.b2a_hex(massage_bin).decode('utf-8')
LOG.info('massage:' + massage)
if format == 'human':
massage_tmp = ' '.join(re.findall('.{2}', massage))
massage = re.findall('.{24}', massage_tmp)
massage.append(massage_tmp[(len(massage) * 24):])
return flask.jsonify({'bin': massage})
else:
return flask.jsonify({
'status': False,
'code': 'please check your post data'
})


@blueprint.route('/peer/<peer_ip>/send/bin_update', methods=['POST'])
@auth.login_required
@api_utils.log_request
@api_utils.makesure_peer_establish
def send_bin_update(peer_ip):
format = flask.request.args.get('format') or None
json_request = flask.request.get_json()
bin = json_request.get('binary_data') or None
try:
if bin:
if format == 'human':
bin = ''.join(bin).replace(' ', '')
elif not isinstance(bin, str):
raise TypeError('arg type should be string')
massage = binascii.a2b_hex(bin)
return flask.jsonify(api_utils.send_bin_update(peer_ip, massage))
else:
return flask.jsonify({
'status': False,
'code': 'please input some string'
})
except binascii.Error as e:
LOG.error(e)
return flask.jsonify({
'status': False,
'code': 'please input even length string'
})
except Exception as e:
LOG.error(e)
return flask.jsonify({
'status': False,
'code': 'please check your post data'
})
36 changes: 31 additions & 5 deletions yabgp/core/protocol.py
Expand Up @@ -322,6 +322,32 @@ def send_update(self, msg):
LOG.error(e)
return False

def construct_update_to_bin(self, msg):
"""
construct update message to binary
:param msg: message dictionary
:return:
"""
try:
msg_update = Update().construct(msg, self.fourbytesas, self.add_path_ipv4_send)
return msg_update
except Exception as e:
LOG.error(e)
return "construct failed"

def send_bin_update(self, msg):
"""
send binary update message to the peer
:param msg: message dictionary
:return:
"""
try:
reactor.callFromThread(self.write_tcp_thread, msg)
return True
except Exception as e:
LOG.error(e)
return False

def write_tcp_thread(self, msg):
self.transport.write(msg)

Expand Down Expand Up @@ -624,9 +650,9 @@ def update_send_version(self, peer_ip, attr, nlri, withdraw):
del value14['nlri']
key = "{"
for k in sorted(prefix.keys()):
key += '"'+k+'"'
key += '"' + k + '"'
key += ':'
key += '"'+str(prefix[k])+'"'
key += '"' + str(prefix[k]) + '"'
key += ','
key = key[:-1]
key += "}"
Expand Down Expand Up @@ -666,9 +692,9 @@ def update_send_version(self, peer_ip, attr, nlri, withdraw):
del value14['nlri']
key = "{"
for k in sorted(prefix.keys()):
key += '"'+k+'"'
key += '"' + k + '"'
key += ':'
key += '"'+str(prefix[k])+'"'
key += '"' + str(prefix[k]) + '"'
key += ','
key = key[:-1]
key += "}"
Expand Down Expand Up @@ -766,7 +792,7 @@ def update_receive_verion(self, attr, nlri, withdraw):
del value14['nlri']
key = "{"
for k in sorted(prefix.keys()):
key += '"'+k+'"'
key += '"' + k + '"'
key += ':'
key += '"' + str(prefix[k]) + '"'
key += ','
Expand Down
1 change: 1 addition & 0 deletions yabgp/message/attribute/linkstate/__init__.py
Expand Up @@ -50,6 +50,7 @@
from .link.unidirect_residual_bw import UnidirectResidualBw # noqa
from .link.unidirect_avail_bw import UnidirectAvailBw # noqa
from .link.unidirect_bw_util import UnidirectBwUtil # noqa
from .link.extend_admin_group import ExtendedAdminGroup # noqa
from .prefix.prefix_metric import PrefixMetric # noqa
from .prefix.prefix_sid import PrefixSID # noqa
from .prefix.prefix_igp_attr import PrefixIGPAttr # noqa
Expand Down
39 changes: 39 additions & 0 deletions yabgp/message/attribute/linkstate/link/extend_admin_group.py
@@ -0,0 +1,39 @@
# Copyright 2015-2017 Cisco Systems, Inc.
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import binascii

from yabgp.tlv import TLV
from ..linkstate import LinkState

# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | Type | Length |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# // Extended Administrative Groups (variable) //
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


@LinkState.register()
class ExtendedAdminGroup(TLV):
"""
extended administrative groups
"""
TYPE = 1173
TYPE_STR = 'extended_admin_group'

@classmethod
def unpack(cls, data):
return cls(value=binascii.b2a_hex(data).decode('utf-8'))

0 comments on commit 99ccef9

Please sign in to comment.