576124794c067e8fb04e3af84105e7ce95e1ecca
[csit.git] / resources / libraries / python / honeycomb / HcAPIKwBridgeDomain.py
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:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
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.
13
14 """Keywords to manipulate bridge domain configuration using Honeycomb REST API.
15
16 The keywords make possible to put and get configuration data and to get
17 operational data.
18 """
19
20 from resources.libraries.python.HTTPRequest import HTTPCodes
21 from resources.libraries.python.honeycomb.HoneycombSetup import HoneycombError
22 from resources.libraries.python.honeycomb.HoneycombUtil \
23     import DataRepresentation
24 from resources.libraries.python.honeycomb.HoneycombUtil \
25     import HoneycombUtil as HcUtil
26
27
28 class BridgeDomainKeywords(object):
29     """Keywords to manipulate bridge domain configuration.
30
31     Implements keywords which get configuration and operational data about
32     bridge domains and put the bridge domains' parameters using Honeycomb REST
33     API.
34     """
35
36     PARAMS = ("flood", "forward", "learn", "unknown-unicast-flood",
37               "arp-termination")
38
39     def __init__(self):
40         pass
41
42     @staticmethod
43     def _configure_bd(node, bd_name, data,
44                       data_representation=DataRepresentation.JSON):
45         """Send bridge domain configuration data and check the response.
46
47         :param node: Honeycomb node.
48         :param bd_name: The name of bridge domain.
49         :param data: Configuration data to be sent in PUT request.
50         :param data_representation: How the data is represented.
51         :type node: dict
52         :type bd_name: str
53         :type data: dict
54         :type data_representation: DataRepresentation
55         :returns: Content of response.
56         :rtype: bytearray
57         :raises HoneycombError: If the status code in response on PUT is not
58             200 = OK.
59         """
60
61         status_code, resp = HcUtil.\
62             put_honeycomb_data(node, "config_bridge_domain", data,
63                                data_representation=data_representation)
64         if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
65             raise HoneycombError(
66                 "The configuration of bridge domain '{0}' was not successful. "
67                 "Status code: {1}.".format(bd_name, status_code))
68         return resp
69
70     @staticmethod
71     def _set_bd_properties(node, bd_name, path, new_value=None):
72         """Set bridge domain properties.
73
74         This method reads bridge domain configuration data, creates, changes or
75         removes the requested data and puts it back to Honeycomb.
76
77         :param node: Honeycomb node.
78         :param bd_name: The name of bridge domain.
79         :param path:  Path to data we want to change, create or remove.
80         :param new_value: The new value to be set. If None, the item will be
81             removed.
82         :type node: dict
83         :type bd_name: str
84         :type path: tuple
85         :type new_value: str, dict or list
86         :returns: Content of response.
87         :rtype: bytearray
88         :raises HoneycombError: If it is not possible to get or set the data.
89         """
90
91         status_code, resp = HcUtil.\
92             get_honeycomb_data(node, "config_bridge_domain")
93         if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
94             raise HoneycombError(
95                 "Not possible to get configuration information about the "
96                 "bridge domains. Status code: {0}.".format(status_code))
97
98         if new_value:
99             new_data = HcUtil.set_item_value(resp, path, new_value)
100         else:
101             new_data = HcUtil.remove_item(resp, path)
102
103         return BridgeDomainKeywords._configure_bd(node, bd_name, new_data)
104
105     @staticmethod
106     def _create_bd_structure(bd_name, **kwargs):
107         """Create the bridge domain data structure as it is expected by
108         Honeycomb REST API.
109
110         :param bd_name: Bridge domain name.
111         :param kwargs: Parameters and their values. The accepted parameters are
112             defined in BridgeDomainKeywords.PARAMS.
113         :type bd_name: str
114         :type kwargs: dict
115         :returns: Bridge domain data structure.
116         :rtype: dict
117         """
118
119         bd_structure = {"name": bd_name}
120
121         for param, value in kwargs.items():
122             if param not in BridgeDomainKeywords.PARAMS:
123                 raise HoneycombError("The parameter {0} is invalid.".
124                                      format(param))
125             bd_structure[param] = str(value)
126
127         return bd_structure
128
129     @staticmethod
130     def get_all_bds_cfg_data(node):
131         """Get configuration data about all bridge domains from Honeycomb.
132
133         :param node: Honeycomb node.
134         :type node: dict
135         :returns: Configuration data about all bridge domains from Honeycomb.
136         :rtype: list
137         :raises HoneycombError: If it is not possible to get configuration data.
138         """
139
140         status_code, resp = HcUtil.\
141             get_honeycomb_data(node, "config_bridge_domain")
142         if status_code != HTTPCodes.OK:
143             raise HoneycombError(
144                 "Not possible to get configuration information about the "
145                 "bridge domains. Status code: {0}.".format(status_code))
146         try:
147             return resp["bridge-domains"]["bridge-domain"]
148
149         except (KeyError, TypeError):
150             return []
151
152     @staticmethod
153     def get_bd_cfg_data(node, bd_name):
154         """Get configuration data about the given bridge domain from Honeycomb.
155
156         :param node: Honeycomb node.
157         :param bd_name: The name of bridge domain.
158         :type node: dict
159         :type bd_name: str
160         :returns: Configuration data about the given bridge domain from
161             Honeycomb.
162         :rtype: dict
163         """
164
165         intfs = BridgeDomainKeywords.get_all_bds_cfg_data(node)
166         for intf in intfs:
167             if intf["name"] == bd_name:
168                 return intf
169         return {}
170
171     @staticmethod
172     def get_all_bds_oper_data(node):
173         """Get operational data about all bridge domains from Honeycomb.
174
175         :param node: Honeycomb node.
176         :type node: dict
177         :returns: Operational data about all bridge domains from Honeycomb.
178         :rtype: list
179         :raises HoneycombError: If it is not possible to get operational data.
180         """
181
182         status_code, resp = HcUtil.\
183             get_honeycomb_data(node, "oper_bridge_domains")
184         if status_code != HTTPCodes.OK:
185             raise HoneycombError(
186                 "Not possible to get operational information about the "
187                 "bridge domains. Status code: {0}.".format(status_code))
188         try:
189             return resp["bridge-domains-state"]["bridge-domain"]
190
191         except (KeyError, TypeError):
192             return []
193
194     @staticmethod
195     def get_bd_oper_data(node, bd_name):
196         """Get operational data about the given bridge domain from Honeycomb.
197
198         :param node: Honeycomb node.
199         :param bd_name: The name of bridge domain.
200         :type node: dict
201         :type bd_name: str
202         :returns: Operational data about the given bridge domain from Honeycomb.
203         :rtype: dict
204         """
205
206         intfs = BridgeDomainKeywords.get_all_bds_oper_data(node)
207         for intf in intfs:
208             if intf["name"] == bd_name:
209                 return intf
210         return {}
211
212     @staticmethod
213     def add_first_bd(node, bd_name, **kwargs):
214         """Add the first bridge domain.
215
216         If there are any other bridge domains configured, they will be removed.
217
218         :param node: Honeycomb node.
219         :param bd_name: Bridge domain name.
220         :param kwargs: Parameters and their values. The accepted parameters are
221             defined in BridgeDomainKeywords.PARAMS
222         :type node: dict
223         :type bd_name: str
224         :type kwargs: dict
225         :returns: Bridge domain data structure.
226         :rtype: dict
227         """
228
229         new_bd = BridgeDomainKeywords._create_bd_structure(bd_name, **kwargs)
230         bridge_domain = {"bridge-domains": {"bridge-domain": [new_bd, ]}}
231         return BridgeDomainKeywords._configure_bd(node, bd_name, bridge_domain)
232
233     @staticmethod
234     def add_bd(node, bd_name, **kwargs):
235         """Add a bridge domain.
236
237         :param node: Honeycomb node.
238         :param bd_name: Bridge domain name.
239         :param kwargs: Parameters and their values. The accepted parameters are
240             defined in BridgeDomainKeywords.PARAMS
241         :type node: dict
242         :type bd_name: str
243         :type kwargs: dict
244         :returns: Bridge domain data structure.
245         :rtype: dict
246         """
247
248         path = ("bridge-domains", "bridge-domain")
249         new_bd = BridgeDomainKeywords._create_bd_structure(bd_name, **kwargs)
250         bridge_domain = [new_bd, ]
251         return BridgeDomainKeywords._set_bd_properties(node, bd_name, path,
252                                                        bridge_domain)
253
254     @staticmethod
255     def remove_all_bridge_domains(node):
256         """Remove all bridge domains.
257
258         :param node: Honeycomb node.
259         :type node: dict
260         :returns: Content of response.
261         :rtype: bytearray
262         :raises HoneycombError: If it is not possible to remove all bridge
263             domains.
264         """
265
266         data = {"bridge-domains": {"bridge-domain": []}}
267
268         status_code, resp = HcUtil.\
269             put_honeycomb_data(node, "config_bridge_domain", data)
270
271         if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
272             raise HoneycombError("Not possible to remove all bridge domains. "
273                                  "Status code: {0}.".format(status_code))
274         return resp
275
276     @staticmethod
277     def remove_bridge_domain(node, bd_name):
278         """Remove a bridge domain.
279
280         :param node:  Honeycomb node.
281         :param bd_name: The name of bridge domain to be removed.
282         :type node: dict
283         :type bd_name: str
284         :returns: Content of response.
285         :rtype: bytearray
286         :raises HoneycombError: If it is not possible to remove the bridge
287             domain.
288         """
289
290         path = ("bridge-domains", ("bridge-domain", "name", bd_name))
291
292         status_code, resp = HcUtil.\
293             get_honeycomb_data(node, "config_bridge_domain")
294         if status_code != HTTPCodes.OK:
295             raise HoneycombError(
296                 "Not possible to get configuration information about the "
297                 "bridge domains. Status code: {0}.".format(status_code))
298
299         new_data = HcUtil.remove_item(resp, path)
300         status_code, resp = HcUtil.\
301             put_honeycomb_data(node, "config_bridge_domain", new_data)
302         if status_code != HTTPCodes.OK:
303             raise HoneycombError("Not possible to remove bridge domain {0}. "
304                                  "Status code: {1}.".
305                                  format(bd_name, status_code))
306         return resp
307
308     @staticmethod
309     def configure_bridge_domain(node, bd_name, param, value):
310         """Configure a bridge domain.
311
312         :param node: Honeycomb node.
313         :param bd_name: Bridge domain name.
314         :param param: Parameter to set, change or remove. The accepted
315             parameters are defined in BridgeDomainKeywords.PARAMS
316         :param value: The new value to be set, change or remove. If None, the
317             item will be removed.
318         :type node: dict
319         :type bd_name: str
320         :type param: str
321         :type value: str
322         :returns: Content of response.
323         :rtype: bytearray
324         """
325
326         if param not in BridgeDomainKeywords.PARAMS:
327             raise HoneycombError("The parameter {0} is invalid.".format(param))
328
329         path = ("bridge-domains", ("bridge-domain", "name", bd_name), param)
330         return BridgeDomainKeywords.\
331             _set_bd_properties(node, bd_name, path, value)
332
333     @staticmethod
334     def add_l2_fib_entry(node, bd_name, l2_fib_entry):
335         """Add an L2 FIB entry to the bridge domain's list of L2 FIB entries.
336
337         :param node: Honeycomb node.
338         :param bd_name: Bridge domain's name.
339         :param l2_fib_entry: L2 FIB entry to be added to the L2 FIB table.
340         :type node: dict
341         :type bd_name: str
342         :type l2_fib_entry: dict
343         :returns: Content of response.
344         :rtype: bytearray
345         """
346
347         path = ("bridge-domains",
348                 ("bridge-domain", "name", bd_name),
349                 "l2-fib-table",
350                 "l2-fib-entry")
351
352         new_l2_fib_entry = [l2_fib_entry, ]
353         return BridgeDomainKeywords._set_bd_properties(
354             node, bd_name, path, new_l2_fib_entry)
355
356     @staticmethod
357     def modify_l2_fib_entry(node, bd_name, mac, param, value):
358         """Modify an existing L2 FIB entry in the bridge domain's L2 FIB table.
359         The L2 FIB entry is specified by MAC address.
360
361         :param node: Honeycomb node.
362         :param bd_name: Bridge domain's name.
363         :param mac: MAC address used as the key in L2 FIB data structure.
364         :param param: The parameter to be modified.
365         :param value: The new value of the parameter.
366         :type node: dict
367         :type bd_name: str
368         :type mac: str
369         :type param: str
370         :type value: str or int
371         :returns: Content of response.
372         :rtype: bytearray
373         """
374
375         path = ("bridge-domains",
376                 ("bridge-domain", "name", bd_name),
377                 "l2-fib-table",
378                 ("l2-fib-entry", "phys-address", mac),
379                 param)
380
381         return BridgeDomainKeywords._set_bd_properties(
382             node, bd_name, path, value)
383
384     @staticmethod
385     def remove_l2_fib_entry(node, bd_name, mac):
386         """Remove an L2 FIB entry from bridge domain's L2 FIB table. The
387         entry is specified by MAC address.
388
389         :param node: Honeycomb node.
390         :param bd_name: Bridge domain's name.
391         :param mac: MAC address used as the key in L2 FIB data structure.
392         :type node: dict
393         :type bd_name: str
394         :type mac: str
395         :returns: Content of response.
396         :rtype: bytearray
397         :raises HoneycombError: If it is not possible to remove the specified
398             entry.
399         """
400
401         path = ("bridge-domains",
402                 ("bridge-domain", "name", bd_name),
403                 "l2-fib-table",
404                 ("l2-fib-entry", "phys-address", str(mac)))
405
406         status_code, resp = HcUtil.\
407             get_honeycomb_data(node, "config_bridge_domain")
408         if status_code != HTTPCodes.OK:
409             raise HoneycombError("Not possible to get configuration information"
410                                  " about the L2 FIB entry {0} from bridge "
411                                  "domain {1}. Status code: {2}.".
412                                  format(mac, bd_name, status_code))
413
414         new_data = HcUtil.remove_item(resp, path)
415         status_code, resp = HcUtil.\
416             put_honeycomb_data(node, "config_bridge_domain", new_data)
417         if status_code != HTTPCodes.OK:
418             raise HoneycombError("Not possible to remove L2 FIB entry {0} from "
419                                  "bridge domain {1}. Status code: {2}.".
420                                  format(mac, bd_name, status_code))
421         return resp
422
423
424     @staticmethod
425     def remove_all_l2_fib_entries(node, bd_name):
426         """Remove all entries from the bridge domain's L2 FIB table.
427
428         :param node: Honeycomb node.
429         :param bd_name: Bridge domain's name.
430         :type node: dict
431         :type bd_name: str
432         :returns: Content of response.
433         :rtype: bytearray
434         """
435
436         path = ("bridge-domains",
437                 ("bridge-domain", "name", bd_name),
438                 "l2-fib-table")
439
440         return BridgeDomainKeywords._set_bd_properties(
441             node, bd_name, path, None)
442
443     @staticmethod
444     def get_all_l2_fib_entries(node, bd_name):
445         """Retrieves all entries from the bridge domain's L2 FIB table.
446
447         :param node: Honeycomb node.
448         :param bd_name: Bridge domain's name.
449         :type node: dict
450         :type bd_name: str
451         :returns: Bridge domain's L2 FIB table or empty list if the table does
452             not exist or it is empty.
453         :rtype: list
454         """
455
456         bd_data = BridgeDomainKeywords.get_bd_oper_data(node, bd_name)
457         try:
458             return bd_data["l2-fib-table"]["l2-fib-entry"]
459         except KeyError:
460             return []
461
462     @staticmethod
463     def get_l2_fib_entry(node, bd_name, mac):
464         """Retrieves an entry from bridge domain's L2 FIB table. The entry is
465         specified by MAC address.
466
467         :param node: Honeycomb node.
468         :param bd_name: Bridge domain's name.
469         :param mac: MAC address used as the key in L2 FIB data structure.
470         :type node: dict
471         :type bd_name: str
472         :type mac: str
473         :returns: The requested entry from bridge domain's L2 FIB table or empty
474             dictionary if it does not exist in the L2 FIB table.
475         :rtype: dict
476         """
477
478         l2_fib = BridgeDomainKeywords.get_all_l2_fib_entries(node, bd_name)
479         for entry in l2_fib:
480             if entry["phys-address"] == mac:
481                 return entry
482         return {}