1 # Copyright (c) 2018 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """Keywords to manipulate BGP configuration using Honeycomb REST API."""
16 from resources.libraries.python.Constants import Constants as Const
17 from resources.libraries.python.HTTPRequest import HTTPCodes
18 from resources.libraries.python.honeycomb.HoneycombSetup import HoneycombError
19 from resources.libraries.python.honeycomb.HoneycombUtil \
20 import HoneycombUtil as HcUtil
23 class BGPKeywords(object):
24 """Keywords to manipulate BGP configuration.
26 Implements keywords which read configuration and operational data for
27 the BGP feature, and configure BGP parameters using Honeycomb REST API.
35 def _configure_bgp_peer(node, path, data=None):
36 """Send BGP peer configuration data and check the response.
38 :param node: Honeycomb node.
39 :param path: Additional path to append to the base BGP config path.
40 :param data: Configuration data to be sent in PUT request.
44 :returns: Content of response.
46 :raises HoneycombError: If the status code in response to PUT is not
47 200 = OK or 201 = ACCEPTED.
51 status_code, resp = HcUtil. \
52 delete_honeycomb_data(node, "config_bgp_peer", path)
54 status_code, resp = HcUtil.\
55 put_honeycomb_data(node, "config_bgp_peer", data, path)
56 if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
58 "The configuration of BGP peer was not successful. "
59 "Status code: {0}.".format(status_code))
63 def _configure_bgp_route(node, path, data=None):
64 """Send BGP route configuration data and check the response.
66 :param node: Honeycomb node.
67 :param path: Additional path to append to the base BGP config path.
68 :param data: Configuration data to be sent in PUT request.
72 :returns: Content of response.
74 :raises HoneycombError: If the status code in response to PUT is not
75 200 = OK or 201 = ACCEPTED.
79 status_code, resp = HcUtil. \
80 delete_honeycomb_data(node, "config_bgp_route", path)
82 status_code, resp = HcUtil. \
83 put_honeycomb_data(node, "config_bgp_route", data, path)
84 if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
86 "The configuration of BGP route was not successful. "
87 "Status code: {0}.".format(status_code))
91 def get_full_bgp_configuration(node):
92 """Get BGP configuration from the node.
94 :param node: Honeycomb node.
96 :returns: BGP configuration data.
98 :raises HoneycombError: If the status code in response is not 200 = OK.
101 status_code, resp = HcUtil. \
102 get_honeycomb_data(node, "config_bgp_peer")
103 if status_code != HTTPCodes.OK:
104 raise HoneycombError(
105 "Not possible to get configuration information about BGP."
106 " Status code: {0}.".format(status_code))
110 def get_bgp_peer(node, address, datastore='config'):
111 """Get BGP configuration of the specified peer from the node.
113 :param node: Honeycomb node.
114 :param address: IP address of the peer.
115 :param datastore: Get data from config or operational datastore.
119 :returns: BGP peer configuration data.
121 :raises HoneycombError: If the status code in response is not 200 = OK.
124 path = "bgp-openconfig-extensions:neighbors/" \
125 "neighbor/{0}".format(address)
126 if datastore != "operational":
127 url = "config_bgp_peer"
130 path = "peer/bgp:%2F%2F{0}".format(address)
131 status_code, resp = HcUtil. \
132 get_honeycomb_data(node, url, path)
133 if status_code != HTTPCodes.OK:
134 raise HoneycombError(
135 "Not possible to get configuration information about the BGP"
136 " peer. Status code: {0}.".format(status_code))
140 def add_bgp_peer(node, address, data):
141 """Configure a BGP peer on the node.
143 :param node: Honeycomb node.
144 :param address: IP address of the peer.
145 :param data: Peer configuration data.
149 :returns: Content of response.
153 path = "bgp-openconfig-extensions:neighbors/neighbor/{address}".format(
155 return BGPKeywords._configure_bgp_peer(node, path, data)
158 def remove_bgp_peer(node, address):
159 """Remove a BGP peer from the configuration.
161 :param node: Honeycomb node.
162 :param address: IP address of the peer.
165 :returns: Content of response.
169 path = "bgp-openconfig-extensions:neighbors/neighbor/{address}".format(
171 return BGPKeywords._configure_bgp_peer(node, path)
174 def configure_bgp_route(node, peer_address, data, route_address,
176 """Configure a route for the BGP peer specified by peer IP address.
178 :param node: Honeycomb node.
179 :param peer_address: IP address of the BGP peer.
180 :param data: Route configuration data.
181 :param route_address: IP address of the route.
182 :param index: Index number of the route within specified peer.
183 :param ip_version: IP protocol version. ipv4 or ipv6
185 :type peer_address: str
187 :type route_address: str
189 :type ip_version: str
190 :returns: Content of response.
194 route_address = route_address.replace("/", "%2F")
196 if ip_version.lower() == "ipv4":
197 path = "{0}/tables/bgp-types:ipv4-address-family/" \
198 "bgp-types:unicast-subsequent-address-family/" \
199 "bgp-inet:ipv4-routes/ipv4-route/{1}/{2}" \
200 .format(peer_address, route_address, index)
202 path = "{0}/tables/bgp-types:ipv6-address-family/" \
203 "bgp-types:unicast-subsequent-address-family/" \
204 "bgp-inet:ipv6-routes/ipv6-route/{1}/{2}" \
205 .format(peer_address, route_address, index)
207 return BGPKeywords._configure_bgp_route(node, path, data)
210 def get_bgp_route(node, peer_address, route_address, index, ip_version):
211 """Get all BGP peers from operational data.
213 :param node: Honeycomb node.
214 :param peer_address: IP address of the BGP peer.
215 :param route_address: IP address of the route.
216 :param index: Index number of the route within specified peer.
217 :param ip_version: IP protocol version. ipv4 or ipv6
219 :type peer_address: str
220 :type route_address: str
222 :type ip_version: str
223 :returns: Content of response.
225 :raises HoneycombError: If the status code in response is not 200 = OK.
228 route_address = route_address.replace("/", "%2F")
230 if ip_version.lower() == "ipv4":
231 path = "{0}/tables/bgp-types:ipv4-address-family/" \
232 "bgp-types:unicast-subsequent-address-family/" \
233 "bgp-inet:ipv4-routes/ipv4-route/{1}/{2}" \
234 .format(peer_address, route_address, index)
236 path = "{0}/tables/bgp-types:ipv6-address-family/" \
237 "bgp-types:unicast-subsequent-address-family/" \
238 "bgp-inet:ipv6-routes/ipv6-route/{1}/{2}" \
239 .format(peer_address, route_address, index)
240 status_code, resp = HcUtil. \
241 get_honeycomb_data(node, "config_bgp_route", path)
242 if status_code != HTTPCodes.OK:
243 raise HoneycombError(
244 "Not possible to get configuration information about the BGP"
245 " route. Status code: {0}.".format(status_code))
250 def get_all_peer_routes(node, peer_address, ip_version):
251 """Get all configured routes for the given BGP peer.
253 :param node: Honeycomb node.
254 :param peer_address: IP address of the peer.
255 :param ip_version: IP protocol version. ipv4 or ipv6
257 :type peer_address: str
258 :type ip_version: str
259 :returns: Content of response.
261 :raises HoneycombError: If the status code in response is not 200 = OK.
264 if ip_version.lower() == "ipv4":
265 path = "{0}/tables/bgp-types:ipv4-address-family/" \
266 "bgp-types:unicast-subsequent-address-family/" \
267 "bgp-inet:ipv4-routes".format(peer_address)
269 path = "{0}/tables/bgp-types:ipv6-address-family/" \
270 "bgp-types:unicast-subsequent-address-family/" \
271 "bgp-inet:ipv6-routes".format(peer_address)
272 status_code, resp = HcUtil. \
273 get_honeycomb_data(node, "config_bgp_route", path)
274 if status_code != HTTPCodes.OK:
275 raise HoneycombError(
276 "Not possible to get configuration information about BGP"
277 " routes. Status code: {0}.".format(status_code))
282 def remove_bgp_route(node, peer_address, route_address, index, ip_version):
283 """Remove the specified BGP route from configuration.
285 :param node: Honeycomb node.
286 :param peer_address: IP address of the BGP peer.
287 :param route_address: IP address of the route.
288 :param index: Index number of the route within specified peer.
289 :param ip_version: IP protocol version. ipv4 or ipv6
291 :type peer_address: str
292 :type route_address: str
294 :type ip_version: str
295 :returns: Content of response.
299 route_address = route_address.replace("/", "%2F")
301 if ip_version.lower() == "ipv4":
302 path = "{0}/tables/bgp-types:ipv4-address-family/" \
303 "bgp-types:unicast-subsequent-address-family/" \
304 "bgp-inet:ipv4-routes/ipv4-route/{1}/{2}" \
305 .format(peer_address, route_address, index)
307 path = "{0}/tables/bgp-types:ipv6-address-family/" \
308 "bgp-types:unicast-subsequent-address-family/" \
309 "bgp-inet:ipv6-routes/ipv6-route/{1}/{2}" \
310 .format(peer_address, route_address, index)
312 return BGPKeywords._configure_bgp_route(node, path)
315 def get_bgp_local_rib(node):
316 """Get local RIB table from the Honeycomb node.
318 :param node: Honeycomb node.
320 :returns: RIB operational data.
322 :raises HoneycombError: If the status code in response is not 200 = OK.
327 status_code, resp = HcUtil. \
328 get_honeycomb_data(node, "oper_bgp", path)
330 if status_code != HTTPCodes.OK:
331 raise HoneycombError(
332 "Not possible to get operational data from BGP local RIB."
333 " Status code: {0}.".format(status_code))
338 def configure_bgp_base(node, ip_address, port, as_number):
339 """Modify BGP config file. Requires a restart of Honeycomb to take
342 :param node: Honeycomb node.
343 :param ip_address: BGP peer identifier/binding address.
344 :param port: BGP binding port.
345 :param as_number: Autonomous System ID number.
347 :type ip_address: str
350 :raises HoneycombError: If modifying the configuration fails.
353 from resources.libraries.python.ssh import SSH
356 '\\"bgp-binding-address\\"': '\\"{0}\\"'.format(ip_address),
357 '\\"bgp-port\\"': port,
358 '\\"bgp-as-number\\"': as_number}
360 path = "{0}/config/bgp.json".format(Const.REMOTE_HC_DIR)
362 for key, value in config.items():
364 replace = '"{0}": "{1}",'.format(key, value)
366 argument = '"/{0}/c\\ {1}"'.format(find, replace)
367 command = "sed -i {0} {1}".format(argument, path)
371 (ret_code, _, stderr) = ssh.exec_command_sudo(command)
373 raise HoneycombError("Failed to modify configuration on "
374 "node {0}, {1}".format(node, stderr))
377 def compare_rib_tables(data, ref):
378 """Compare provided RIB table with reference. All reference entries must
379 be present in data. Data entries not present in reference are ignored.
381 :param data: Data from Honeycomb node.
382 :param ref: Reference data to compare against.
385 :raises HoneycombError: If the tables do not match.
388 # Remove runtime attributes from data
390 item.pop("attributes", "")
394 raise HoneycombError(
395 "RIB entry {0} not found in operational data {1}."