f156f096e3d67a6695a4b8669f5d6ba27ff7b759
[csit.git] / resources / libraries / python / honeycomb / HcAPIKwBridgeDomain.py
1 # Copyright (c) 2016 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         :return: 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 != HTTPCodes.OK:
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         :return: 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 != HTTPCodes.OK:
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         :return: 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         :return: 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         :return: 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         :return: 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"]["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         :return: 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         :return: Bridge domain data structure.
226         :rtype: dict
227         """
228
229         path = ("bridge-domains", )
230         new_bd = BridgeDomainKeywords._create_bd_structure(bd_name, **kwargs)
231         bridge_domain = {"bridge-domain": [new_bd, ]}
232         return BridgeDomainKeywords._set_bd_properties(node, bd_name, path,
233                                                        bridge_domain)
234
235     @staticmethod
236     def add_bd(node, bd_name, **kwargs):
237         """Add a bridge domain.
238
239         :param node: Honeycomb node.
240         :param bd_name: Bridge domain name.
241         :param kwargs: Parameters and their values. The accepted parameters are
242         defined in BridgeDomainKeywords.PARAMS
243         :type node: dict
244         :type bd_name: str
245         :type kwargs: dict
246         :return: Bridge domain data structure.
247         :rtype: dict
248         """
249
250         path = ("bridge-domains", "bridge-domain")
251         new_bd = BridgeDomainKeywords._create_bd_structure(bd_name, **kwargs)
252         bridge_domain = [new_bd, ]
253         return BridgeDomainKeywords._set_bd_properties(node, bd_name, path,
254                                                        bridge_domain)
255
256     @staticmethod
257     def remove_all_bridge_domains(node):
258         """Remove all bridge domains.
259
260         :param node: Honeycomb node.
261         :type node: dict
262         :return: Content of response.
263         :rtype: bytearray
264         :raises HoneycombError: If it is not possible to remove all bridge
265         domains.
266         """
267
268         data = {"bridge-domains": {"bridge-domain": []}}
269
270         status_code, resp = HcUtil.\
271             put_honeycomb_data(node, "config_bridge_domain", data)
272
273         if status_code != HTTPCodes.OK:
274             raise HoneycombError("Not possible to remove all bridge domains. "
275                                  "Status code: {0}.".format(status_code))
276         return resp
277
278     @staticmethod
279     def remove_bridge_domain(node, bd_name):
280         """Remove a bridge domain.
281
282         :param node:  Honeycomb node.
283         :param bd_name: The name of bridge domain to be removed.
284         :type node: dict
285         :type bd_name: str
286         :return: Content of response.
287         :rtype: bytearray
288         :raises HoneycombError:If it is not possible to remove the bridge
289         domain.
290         """
291
292         path = ("bridge-domains", ("bridge-domain", "name", bd_name))
293
294         status_code, resp = HcUtil.\
295             get_honeycomb_data(node, "config_bridge_domain")
296         if status_code != HTTPCodes.OK:
297             raise HoneycombError(
298                 "Not possible to get configuration information about the "
299                 "bridge domains. Status code: {0}.".format(status_code))
300
301         new_data = HcUtil.remove_item(resp, path)
302         status_code, resp = HcUtil.\
303             put_honeycomb_data(node, "config_bridge_domain", new_data)
304         if status_code != HTTPCodes.OK:
305             raise HoneycombError("Not possible to remove bridge domain {0}. "
306                                  "Status code: {1}.".
307                                  format(bd_name, status_code))
308         return resp
309
310     @staticmethod
311     def configure_bridge_domain(node, bd_name, param, value):
312         """Configure a bridge domain.
313
314         :param node: Honeycomb node.
315         :param bd_name: Bridge domain name.
316         :param param: Parameter to set, change or remove. The accepted
317         parameters are defined in BridgeDomainKeywords.PARAMS
318         :param value: The new value to be set, change or remove. If None, the
319         item will be removed.
320         :type node: dict
321         :type bd_name: str
322         :type param: str
323         :type value: str
324         :return: Content of response.
325         :rtype: bytearray
326         """
327
328         if param not in BridgeDomainKeywords.PARAMS:
329             raise HoneycombError("The parameter {0} is invalid.".format(param))
330
331         path = ("bridge-domains", ("bridge-domain", "name", bd_name), param)
332         return BridgeDomainKeywords.\
333             _set_bd_properties(node, bd_name, path, value)
334
335     @staticmethod
336     def add_l2_fib_entry(node, bd_name, l2_fib_entry):
337         """Add an L2 FIB entry to the bridge domain's list of L2 FIB entries.
338
339         :param node: Honeycomb node.
340         :param bd_name: Bridge domain's name.
341         :param l2_fib_entry: L2 FIB entry to be added to the L2 FIB table.
342         :type node: dict
343         :type bd_name: str
344         :type l2_fib_entry: dict
345         :return: Content of response.
346         :rtype: bytearray
347         """
348
349         path = ("bridge-domains",
350                 ("bridge-domain", "name", bd_name),
351                 "l2-fib-table",
352                 "l2-fib-entry")
353
354         new_l2_fib_entry = [l2_fib_entry, ]
355         return BridgeDomainKeywords._set_bd_properties(
356             node, bd_name, path, new_l2_fib_entry)
357
358     @staticmethod
359     def modify_l2_fib_entry(node, bd_name, mac, param, value):
360         """Modify an existing L2 FIB entry in the bridge domain's L2 FIB table.
361         The L2 FIB entry is specified by MAC address.
362
363         :param node: Honeycomb node.
364         :param bd_name: Bridge domain's name.
365         :param mac: MAC address used as the key in L2 FIB data structure.
366         :param param: The parameter to be modified.
367         :param value: The new value of the parameter.
368         :type node: dict
369         :type bd_name: str
370         :type mac: str
371         :type param: str
372         :type value: str or int
373         :return: Content of response.
374         :rtype: bytearray
375         """
376
377         path = ("bridge-domains",
378                 ("bridge-domain", "name", bd_name),
379                 "l2-fib-table",
380                 ("l2-fib-entry", "phys-address", mac),
381                 param)
382
383         return BridgeDomainKeywords._set_bd_properties(
384             node, bd_name, path, value)
385
386     @staticmethod
387     def remove_l2_fib_entry(node, bd_name, mac):
388         """Remove an L2 FIB entry from bridge domain's L2 FIB table. The
389         entry is specified by MAC address.
390
391         :param node: Honeycomb node.
392         :param bd_name: Bridge domain's name.
393         :param mac: MAC address used as the key in L2 FIB data structure.
394         :type node: dict
395         :type bd_name: str
396         :type mac: str
397         :return: Content of response.
398         :rtype: bytearray
399         :raises HoneycombError: If it is not possible to remove the specified
400         entry.
401         """
402
403         path = ("bridge-domains",
404                 ("bridge-domain", "name", bd_name),
405                 "l2-fib-table",
406                 ("l2-fib-entry", "phys-address", str(mac)))
407
408         status_code, resp = HcUtil.\
409             get_honeycomb_data(node, "config_bridge_domain")
410         if status_code != HTTPCodes.OK:
411             raise HoneycombError("Not possible to get configuration information"
412                                  " about the L2 FIB entry {0} from bridge "
413                                  "domain {1}. Status code: {2}.".
414                                  format(mac, bd_name, status_code))
415
416         new_data = HcUtil.remove_item(resp, path)
417         status_code, resp = HcUtil.\
418             put_honeycomb_data(node, "config_bridge_domain", new_data)
419         if status_code != HTTPCodes.OK:
420             raise HoneycombError("Not possible to remove L2 FIB entry {0} from "
421                                  "bridge domain {1}. Status code: {2}.".
422                                  format(mac, bd_name, status_code))
423         return resp
424
425
426     @staticmethod
427     def remove_all_l2_fib_entries(node, bd_name):
428         """Remove all entries from the bridge domain's L2 FIB table.
429
430         :param node: Honeycomb node.
431         :param bd_name: Bridge domain's name.
432         :type node: dict
433         :type bd_name: str
434         :return: Content of response.
435         :rtype: bytearray
436         """
437
438         path = ("bridge-domains",
439                 ("bridge-domain", "name", bd_name),
440                 "l2-fib-table")
441
442         return BridgeDomainKeywords._set_bd_properties(
443             node, bd_name, path, None)
444
445     @staticmethod
446     def get_all_l2_fib_entries(node, bd_name):
447         """Retrieves all entries from the bridge domain's L2 FIB table.
448
449         :param node: Honeycomb node.
450         :param bd_name: Bridge domain's name.
451         :type node: dict
452         :type bd_name: str
453         :return: Bridge domain's L2 FIB table or empty list if the table does
454         not exist or it is empty.
455         :rtype: list
456         """
457
458         bd_data = BridgeDomainKeywords.get_bd_oper_data(node, bd_name)
459         try:
460             return bd_data["l2-fib-table"]["l2-fib-entry"]
461         except KeyError:
462             return []
463
464     @staticmethod
465     def get_l2_fib_entry(node, bd_name, mac):
466         """Retrieves an entry from bridge domain's L2 FIB table. The entry is
467         specified by MAC address.
468
469         :param node: Honeycomb node.
470         :param bd_name: Bridge domain's name.
471         :param mac: MAC address used as the key in L2 FIB data structure.
472         :type node: dict
473         :type bd_name: str
474         :type mac: str
475         :return: The requested entry from bridge domain's L2 FIB table or empty
476         dictionary if it does not exist in the L2 FIB table.
477         :rtype: dict
478         """
479
480         l2_fib = BridgeDomainKeywords.get_all_l2_fib_entries(node, bd_name)
481         for entry in l2_fib:
482             if entry["phys-address"] == mac:
483                 return entry
484         return {}