Merge "Tools: Improve Stability."
[vswitchperf.git] / vswitches / ovs.py
1 # Copyright 2015-2018 Intel Corporation., Tieto
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #   http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 """VSPERF Open vSwitch base class
16 """
17
18 import os
19 import re
20 import time
21 import datetime
22 import random
23 import socket
24 import netaddr
25 import pexpect
26
27 from conf import settings
28 from src.ovs import OFBridge, flow_key, flow_match
29 from vswitches.vswitch import IVSwitch
30 from tools import tasks
31 from tools.module_manager import ModuleManager
32
33 # enable caching of flows if their number exceeds given limit
34 _CACHE_FLOWS_LIMIT = 10
35
36 # pylint: disable=too-many-public-methods
37 class IVSwitchOvs(IVSwitch, tasks.Process):
38     """Open vSwitch base class implementation
39
40     The method docstrings document only considerations specific to this
41     implementation. For generic information of the nature of the methods,
42     see the interface.
43     """
44     _proc_name = 'ovs-vswitchd'
45
46     def __init__(self):
47         """See IVswitch for general description
48         """
49         super().__init__()
50
51         name, ext = os.path.splitext(settings.getValue('LOG_FILE_VSWITCHD'))
52         rename_vswitchd = "{name}_{uid}{ex}".format(name=name,
53                                                     uid=settings.getValue('LOG_TIMESTAMP'),
54                                                     ex=ext)
55         self._logfile = os.path.join(settings.getValue('RESULTS_PATH'), rename_vswitchd)
56         self._ovsdb_pidfile_path = os.path.join(settings.getValue('TOOLS')['ovs_var_tmp'],
57                                                 "ovsdb-server.pid")
58         self._vswitchd_pidfile_path = os.path.join(settings.getValue('TOOLS')['ovs_var_tmp'],
59                                                    "{}.pid".format(self._proc_name))
60         # sign '|' must be escaped or avoided, otherwise it is handled as 'or' by regex
61         self._expect = r'bridge.INFO.{}'.format(self._proc_name)
62         self._vswitchd_args = ['--pidfile=' + self._vswitchd_pidfile_path,
63                                '--overwrite-pidfile', '--log-file=' + self._logfile]
64         self._cmd_template = ['sudo', '-E', settings.getValue('TOOLS')['ovs-vswitchd']]
65         self._module_manager = ModuleManager()
66         self._flow_template = settings.getValue('OVS_FLOW_TEMPLATE').copy()
67         self._flow_actions = ['output:{}']
68
69         # if routing tables are enabled, then flows should go into table 1
70         # see design document for details about Routing Tables feature
71         if settings.getValue('OVS_ROUTING_TABLES'):
72             # flows should be added into table 1
73             self._flow_template.update({'table':'1', 'priority':'1'})
74             # and chosen port will be propagated via metadata
75             self._flow_actions = ['write_actions(output:{})',
76                                   'write_metadata:{}',
77                                   'goto_table:2']
78
79     def start(self):
80         """ Start ``ovsdb-server`` and ``ovs-vswitchd`` instance.
81
82         :raises: pexpect.EOF, pexpect.TIMEOUT
83         """
84         self._logger.info("Starting vswitchd...")
85
86         # insert kernel modules if required
87         if 'vswitch_modules' in settings.getValue('TOOLS'):
88             self._module_manager.insert_modules(settings.getValue('TOOLS')['vswitch_modules'])
89
90         self._cmd = self._cmd_template + self._vswitchd_args
91
92         # DB must be started before vswitchd
93         self._reset_ovsdb()
94         self._start_ovsdb()
95
96         # DB must be up before vswitchd config is altered or vswitchd started
97         time.sleep(3)
98
99         self.configure()
100
101         try:
102             tasks.Process.start(self)
103             self.relinquish()
104         except (pexpect.EOF, pexpect.TIMEOUT) as exc:
105             self._logger.error("Exception during VSwitch start.")
106             self._kill_ovsdb()
107             raise exc
108
109         self._logger.info("Vswitchd...Started.")
110
111     def restart(self):
112         """ Restart ``ovs-vswitchd`` instance. ``ovsdb-server`` is not restarted.
113
114         :raises: pexpect.EOF, pexpect.TIMEOUT
115         """
116         self._logger.info("Restarting vswitchd...")
117         if os.path.isfile(self._vswitchd_pidfile_path):
118             self._logger.info('Killing ovs-vswitchd...')
119             with open(self._vswitchd_pidfile_path, "r") as pidfile:
120                 vswitchd_pid = pidfile.read().strip()
121                 tasks.terminate_task(vswitchd_pid, logger=self._logger)
122
123         try:
124             tasks.Process.start(self)
125             self.relinquish()
126         except (pexpect.EOF, pexpect.TIMEOUT) as exc:
127             self._logger.error("Exception during VSwitch start.")
128             self._kill_ovsdb()
129             raise exc
130         self._logger.info("Vswitchd...Started.")
131
132     def configure(self):
133         """ Configure vswitchd through ovsdb if needed
134         """
135         pass
136
137     # Method could be a function
138     # pylint: disable=no-self-use
139     def get_version(self):
140         """See IVswitch for general description
141         """
142         # OVS version can be read offline
143         return []
144
145     def stop(self):
146         """See IVswitch for general description
147         """
148         for switch_name in list(self._switches):
149             self.del_switch(switch_name)
150         self._logger.info("Terminating vswitchd...")
151         self.kill()
152         self._switches = {}
153         self._logger.info("Vswitchd...Terminated.")
154
155     def add_switch(self, switch_name, params=None):
156         """See IVswitch for general description
157         """
158         # create and configure new ovs bridge and delete all default flows
159         bridge = OFBridge(switch_name)
160         bridge.create(params)
161         bridge.del_flow({})
162         bridge.set_db_attribute('Open_vSwitch', '.',
163                                 'other_config:max-idle',
164                                 settings.getValue('VSWITCH_FLOW_TIMEOUT'))
165         self._switches[switch_name] = bridge
166         if settings.getValue('OVS_ROUTING_TABLES'):
167             # table#0 - flows designed to force 5 & 13 tuple matches go here
168             flow = {'table':'0', 'priority':'1', 'actions': ['goto_table:1']}
169             bridge.add_flow(flow)
170
171             # table#1 - flows to route packets between ports goes here. The
172             # chosen port is communicated to subsequent tables by setting the
173             # metadata value to the egress port number
174             #
175             # A placeholder - flows are added into this table by deployments
176             #                 or by TestSteps via add_connection() method
177
178             # table#2 - frame modification table. Frame modification flow rules are
179             # isolated in this table so that they can be turned on or off
180             # without affecting the routing or tuple-matching flow rules.
181             flow = {'table':'2', 'priority':'1', 'actions': ['goto_table:3']}
182             bridge.add_flow(flow)
183
184             # table#3 - egress table
185             # (NOTE) Billy O'Mahony - the drop action here actually required in
186             # order to egress the packet. This is the subject of a thread on
187             # ovs-discuss 2015-06-30.
188             flow = {'table':'3', 'priority':'1', 'actions': ['drop']}
189             bridge.add_flow(flow)
190
191     def del_switch(self, switch_name):
192         """See IVswitch for general description
193         """
194         bridge = self._switches[switch_name]
195         bridge.del_flow({})
196         for port in list(bridge.get_ports()):
197             bridge.del_port(port)
198         self._switches.pop(switch_name)
199         bridge.destroy()
200
201     def add_phy_port(self, switch_name):
202         """See IVswitch for general description
203         """
204         raise NotImplementedError
205
206     def add_vport(self, switch_name):
207         """See IVswitch for general description
208         """
209         raise NotImplementedError
210
211     def add_veth_pair_port(self, switch_name=None, remote_switch_name=None,
212                            local_opts=None, remote_opts=None):
213         """Creates veth-pair port between 'switch_name' and 'remote_switch_name'
214
215         """
216         if switch_name is None or remote_switch_name is None:
217             return None
218
219         bridge = self._switches[switch_name]
220         remote_bridge = self._switches[remote_switch_name]
221         pcount = str(self._get_port_count('type=patch'))
222         # NOTE ::: What if interface name longer than allowed width??
223         local_port_name = switch_name + '-' + remote_switch_name + '-' + pcount
224         remote_port_name = remote_switch_name + '-' + switch_name + '-' + pcount
225         local_params = ['--', 'set', 'Interface', local_port_name,
226                         'type=patch',
227                         'options:peer=' + remote_port_name]
228         remote_params = ['--', 'set', 'Interface', remote_port_name,
229                          'type=patch',
230                          'options:peer=' + local_port_name]
231
232         if local_opts is not None:
233             local_params = local_params + local_opts
234
235         if remote_opts is not None:
236             remote_params = remote_params + remote_opts
237
238         local_of_port = bridge.add_port(local_port_name, local_params)
239         remote_of_port = remote_bridge.add_port(remote_port_name, remote_params)
240         return [(local_port_name, local_of_port),
241                 (remote_port_name, remote_of_port)]
242
243     def add_tunnel_port(self, switch_name, remote_ip, tunnel_type='vxlan', params=None):
244         """Creates tunneling port
245         """
246         bridge = self._switches[switch_name]
247         pcount = str(self._get_port_count('type=' + tunnel_type))
248         port_name = tunnel_type + pcount
249         local_params = ['--', 'set', 'Interface', port_name,
250                         'type=' + tunnel_type,
251                         'options:remote_ip=' + remote_ip]
252
253         if params is not None:
254             local_params = local_params + params
255
256         of_port = bridge.add_port(port_name, local_params)
257         return (port_name, of_port)
258
259     def get_ports(self, switch_name):
260         """See IVswitch for general description
261         """
262         bridge = self._switches[switch_name]
263         ports = list(bridge.get_ports().items())
264         return [(name, of_port) for (name, (of_port, _)) in ports]
265
266     def del_port(self, switch_name, port_name):
267         """See IVswitch for general description
268         """
269         bridge = self._switches[switch_name]
270         bridge.del_port(port_name)
271
272     def add_flow(self, switch_name, flow, cache='off'):
273         """See IVswitch for general description
274         """
275         bridge = self._switches[switch_name]
276         bridge.add_flow(flow, cache=cache)
277
278     def del_flow(self, switch_name, flow=None):
279         """See IVswitch for general description
280         """
281         flow = flow or {}
282         bridge = self._switches[switch_name]
283         bridge.del_flow(flow)
284
285     def dump_flows(self, switch_name):
286         """See IVswitch for general description
287         """
288         bridge = self._switches[switch_name]
289         bridge.dump_flows()
290
291     def _prepare_flows(self, operation, switch_name, port1, port2, traffic=None):
292         """Prepare flows for add_connection, del_connection and validate methods
293            It returns a list of flows based on given parameters.
294         """
295         flows = []
296         if operation == 'add':
297             bridge = self._switches[switch_name]
298             flow = self._flow_template.copy()
299             actions = [action.format(bridge.get_ports()[port2][0]) for action in self._flow_actions]
300             flow.update({'in_port': bridge.get_ports()[port1][0], 'actions': actions})
301             # check if stream specific connection(s) should be crated for multistream feature
302             if traffic and traffic['pre_installed_flows'].lower() == 'yes':
303                 for stream in range(traffic['multistream']):
304                     tmp_flow = flow.copy()
305                     # update flow based on trafficgen settings
306                     if traffic['stream_type'] == 'L2':
307                         dst_mac_value = netaddr.EUI(traffic['l2']['dstmac']).value
308                         tmp_mac = netaddr.EUI(dst_mac_value + stream)
309                         tmp_mac.dialect = netaddr.mac_unix_expanded
310                         tmp_flow.update({'dl_dst':tmp_mac})
311                     elif traffic['stream_type'] == 'L3':
312                         dst_ip_value = netaddr.IPAddress(traffic['l3']['dstip']).value
313                         tmp_ip = netaddr.IPAddress(dst_ip_value + stream)
314                         tmp_flow.update({'dl_type':'0x0800', 'nw_dst':tmp_ip})
315                     elif traffic['stream_type'] == 'L4':
316                         tmp_flow.update({'dl_type':'0x0800',
317                                          'nw_proto':socket.getprotobyname(traffic['l3']['proto'].lower()),
318                                          'tp_dst':(traffic['l4']['dstport'] + stream) % 65536})
319                     flows.append(tmp_flow)
320             elif traffic and traffic['flow_type'].lower() == 'ip':
321                 flow.update({'dl_type':'0x0800', 'nw_src':traffic['l3']['srcip'],
322                              'nw_dst':traffic['l3']['dstip']})
323                 flows.append(flow)
324             else:
325                 flows.append(flow)
326         elif operation == 'del' and port1:
327             bridge = self._switches[switch_name]
328             flows.append({'in_port': bridge.get_ports()[port1][0]})
329         else:
330             flows.append({})
331
332         return flows
333
334     def add_connection(self, switch_name, port1, port2, traffic=None):
335         """See IVswitch for general description
336         """
337         flows = self._prepare_flows('add', switch_name, port1, port2, traffic)
338
339         # enable flows caching for large number of flows
340         cache = 'on' if len(flows) > _CACHE_FLOWS_LIMIT else 'off'
341
342         for flow in flows:
343             self.add_flow(switch_name, flow, cache)
344
345         if cache == 'on':
346             self.add_flow(switch_name, [], cache='flush')
347
348     def del_connection(self, switch_name, port1=None, port2=None):
349         """See IVswitch for general description
350         """
351         flows = self._prepare_flows('del', switch_name, port1, port2)
352
353         for flow in flows:
354             self.del_flow(switch_name, flow)
355
356     def dump_connections(self, switch_name):
357         """See IVswitch for general description
358         """
359         self.dump_flows(switch_name)
360
361     def add_route(self, switch_name, network, destination):
362         """See IVswitch for general description
363         """
364         bridge = self._switches[switch_name]
365         bridge.add_route(network, destination)
366
367     def set_tunnel_arp(self, ip_addr, mac_addr, switch_name):
368         """See IVswitch for general description
369         """
370         bridge = self._switches[switch_name]
371         bridge.set_tunnel_arp(ip_addr, mac_addr, switch_name)
372
373     def _get_port_count(self, param):
374         """Returns the number of ports having a certain parameter
375         """
376         cnt = 0
377         for k in self._switches:
378             pparams = [c for (_, (_, c)) in list(self._switches[k].get_ports().items())]
379             phits = [i for i in pparams if param in i]
380             cnt += len(phits)
381
382         if cnt is None:
383             cnt = 0
384         return cnt
385
386     def disable_stp(self, switch_name):
387         """
388         Disable stp protocol on the bridge
389         :param switch_name: bridge to disable stp
390         :return: None
391         """
392         bridge = self._switches[switch_name]
393         bridge.set_stp(False)
394         self._logger.info('Sleeping for 50 secs to allow stp to stop.')
395         time.sleep(50)  # needs time to disable
396
397     def enable_stp(self, switch_name):
398         """
399         Enable stp protocol on the bridge
400         :param switch_name: bridge to enable stp
401         :return: None
402         """
403         bridge = self._switches[switch_name]
404         bridge.set_stp(True)
405         self._logger.info('Sleeping for 50 secs to allow stp to start.')
406         time.sleep(50)  # needs time to enable
407
408     def disable_rstp(self, switch_name):
409         """
410         Disable rstp on the bridge
411         :param switch_name: bridge to disable rstp
412         :return: None
413         """
414         bridge = self._switches[switch_name]
415         bridge.set_rstp(False)
416         self._logger.info('Sleeping for 15 secs to allow rstp to stop.')
417         time.sleep(15)  # needs time to disable
418
419     def enable_rstp(self, switch_name):
420         """
421         Enable rstp on the bridge
422         :param switch_name: bridge to enable rstp
423         :return: None
424         """
425         bridge = self._switches[switch_name]
426         bridge.set_rstp(True)
427         self._logger.info('Sleeping for 15 secs to allow rstp to start.')
428         time.sleep(15)  # needs time to enable
429
430     def kill(self, signal='-15', sleep=10):
431         """Kill ``ovs-vswitchd`` and ``ovs-ovsdb`` instances if they are alive.
432
433         :returns: None
434         """
435         if os.path.isfile(self._vswitchd_pidfile_path):
436             self._logger.info('Killing ovs-vswitchd...')
437             with open(self._vswitchd_pidfile_path, "r") as pidfile:
438                 vswitchd_pid = pidfile.read().strip()
439                 tasks.terminate_task(vswitchd_pid, logger=self._logger)
440
441         self._kill_ovsdb()  # ovsdb must be killed after vswitchd
442
443         # just for case, that sudo envelope has not been terminated yet
444         tasks.Process.kill(self, signal, sleep)
445
446     # helper functions
447
448     def _reset_ovsdb(self):
449         """Reset system for 'ovsdb'.
450
451         :returns: None
452         """
453         self._logger.info('Resetting system after last run...')
454
455         # create a backup of ovs_var_tmp and ovs_etc_tmp; It is
456         # essential for OVS installed from binary packages.
457         self._stamp = '{:%Y%m%d_%H%M%S}_{}'.format(datetime.datetime.now(),
458                                                    random.randrange(1000, 9999))
459         for tmp_dir in ['ovs_var_tmp', 'ovs_etc_tmp']:
460             if os.path.exists(settings.getValue('TOOLS')[tmp_dir]):
461                 orig_dir = os.path.normpath(settings.getValue('TOOLS')[tmp_dir])
462                 self._logger.info('Creating backup of %s directory...', tmp_dir)
463                 tasks.run_task(['sudo', 'mv', orig_dir, '{}.{}'.format(orig_dir, self._stamp)],
464                                self._logger)
465
466         # create fresh tmp dirs
467         tasks.run_task(['sudo', 'mkdir', '-p', settings.getValue('TOOLS')['ovs_var_tmp']], self._logger)
468         tasks.run_task(['sudo', 'mkdir', '-p', settings.getValue('TOOLS')['ovs_etc_tmp']], self._logger)
469
470         self._logger.info('System reset after last run.')
471
472     def _start_ovsdb(self):
473         """Start ``ovsdb-server`` instance.
474
475         :returns: None
476         """
477         ovsdb_tool_bin = settings.getValue('TOOLS')['ovsdb-tool']
478         tasks.run_task(['sudo', ovsdb_tool_bin, 'create',
479                         os.path.join(settings.getValue('TOOLS')['ovs_etc_tmp'], 'conf.db'),
480                         settings.getValue('TOOLS')['ovsschema']],
481                        self._logger,
482                        'Creating ovsdb configuration database...')
483
484         ovsdb_server_bin = settings.getValue('TOOLS')['ovsdb-server']
485
486         tasks.run_background_task(
487             ['sudo', ovsdb_server_bin,
488              '--remote=punix:%s' % os.path.join(settings.getValue('TOOLS')['ovs_var_tmp'], 'db.sock'),
489              '--remote=db:Open_vSwitch,Open_vSwitch,manager_options',
490              '--pidfile=' + self._ovsdb_pidfile_path, '--overwrite-pidfile'],
491             self._logger,
492             'Starting ovsdb-server...')
493
494     def _kill_ovsdb(self):
495         """Kill ``ovsdb-server`` instance.
496
497         :returns: None
498         """
499         if os.path.isfile(self._ovsdb_pidfile_path):
500             with open(self._ovsdb_pidfile_path, "r") as pidfile:
501                 ovsdb_pid = pidfile.read().strip()
502
503             self._logger.info("Killing ovsdb with pid: %s", ovsdb_pid)
504
505             if ovsdb_pid:
506                 tasks.terminate_task(ovsdb_pid, logger=self._logger)
507
508         # restore original content of ovs_var_tmp and ovs_etc_tmp; It is
509         # essential for OVS installed from binary packages.
510         if self._stamp:
511             for tmp_dir in ['ovs_var_tmp', 'ovs_etc_tmp']:
512                 orig_dir = os.path.normpath(settings.getValue('TOOLS')[tmp_dir])
513                 if os.path.exists('{}.{}'.format(orig_dir, self._stamp)):
514                     self._logger.info('Restoring backup of %s directory...', tmp_dir)
515                     tasks.run_task(['sudo', 'rm', '-rf', orig_dir], self._logger)
516                     tasks.run_task(['sudo', 'mv', '{}.{}'.format(orig_dir, self._stamp), orig_dir],
517                                    self._logger)
518
519     @staticmethod
520     def get_db_sock_path():
521         """Method returns location of db.sock file
522
523         :returns: path to db.sock file.
524         """
525         return os.path.join(settings.getValue('TOOLS')['ovs_var_tmp'], 'db.sock')
526
527     #
528     # validate methods required for integration testcases
529     #
530     def validate_add_switch(self, _dummy_result, switch_name, _dummy_params=None):
531         """Validate - Create a new logical switch with no ports
532         """
533         bridge = self._switches[switch_name]
534         output = bridge.run_vsctl(['show'], check_error=True)
535         assert not output[1]  # there shouldn't be any stderr, but in case
536         assert re.search('Bridge ["\']?%s["\']?' % switch_name, output[0]) is not None
537         return True
538
539     # Method could be a function
540     # pylint: disable=no-self-use
541     def validate_del_switch(self, _dummy_result, switch_name):
542         """Validate removal of switch
543         """
544         bridge = OFBridge('tmp')
545         output = bridge.run_vsctl(['show'], check_error=True)
546         assert not output[1]  # there shouldn't be any stderr, but in case
547         assert re.search('Bridge ["\']?%s["\']?' % switch_name, output[0]) is None
548         return True
549
550     def validate_add_phy_port(self, result, switch_name):
551         """ Validate that physical port was added to bridge.
552         """
553         bridge = self._switches[switch_name]
554         output = bridge.run_vsctl(['show'], check_error=True)
555         assert not output[1]  # there shouldn't be any stderr, but in case
556         assert re.search('Port ["\']?%s["\']?' % result[0], output[0]) is not None
557         assert re.search('Interface ["\']?%s["\']?' % result[0], output[0]) is not None
558         return True
559
560     def validate_add_vport(self, result, switch_name):
561         """ Validate that virtual port was added to bridge.
562         """
563         return self.validate_add_phy_port(result, switch_name)
564
565     def validate_del_port(self, _dummy_result, switch_name, port_name):
566         """ Validate that port_name was removed from bridge.
567         """
568         bridge = self._switches[switch_name]
569         output = bridge.run_vsctl(['show'], check_error=True)
570         assert not output[1]  # there shouldn't be any stderr, but in case
571         assert 'Port "%s"' % port_name not in output[0]
572         return True
573
574     def validate_add_connection(self, result, switch_name, port1, port2, traffic=None):
575         """ Validate that connection was added
576         """
577         for flow in self._prepare_flows('add', switch_name, port1, port2, traffic):
578             if not self.validate_add_flow(result, switch_name, flow):
579                 return False
580
581         return True
582
583     def validate_del_connection(self, result, switch_name, port1, port2):
584         """ Validate that connection was deleted
585         """
586         for flow in self._prepare_flows('del', switch_name, port1, port2):
587             if not self.validate_del_flow(result, switch_name, flow):
588                 return False
589
590         return True
591
592     def validate_dump_connections(self, _dummy_result, _dummy_switch_name):
593         """ Validate dump connections call
594         """
595         return True
596
597     def validate_add_flow(self, _dummy_result, switch_name, flow, _dummy_cache='off'):
598         """ Validate insertion of the flow into the switch
599         """
600
601         if 'idle_timeout' in flow:
602             del flow['idle_timeout']
603
604         # Note: it should be possible to call `ovs-ofctl dump-flows switch flow`
605         # to verify flow insertion, but it doesn't accept the same flow syntax
606         # as add-flow, so we have to compare it the hard way
607
608         # get dump of flows and compare them one by one
609         flow_src = flow_key(flow)
610         bridge = self._switches[switch_name]
611         output = bridge.run_ofctl(['dump-flows', switch_name], check_error=True)
612         for flow_dump in output[0].split('\n'):
613             if flow_match(flow_dump, flow_src):
614                 # flow was added correctly
615                 return True
616         return False
617
618     def validate_del_flow(self, _dummy_result, switch_name, flow=None):
619         """ Validate removal of the flow
620         """
621         if not flow:
622             # what else we can do?
623             return True
624         return not self.validate_add_flow(_dummy_result, switch_name, flow)
625
626     def validate_dump_flows(self, _dummy_result, _dummy_switch_name):
627         """ Validate call of flow dump
628         """
629         return True
630
631     def validate_disable_rstp(self, _dummy_result, switch_name):
632         """ Validate rstp disable
633         """
634         bridge = self._switches[switch_name]
635         return 'rstp_enable         : false' in ''.join(bridge.bridge_info())
636
637     def validate_enable_rstp(self, _dummy_result, switch_name):
638         """ Validate rstp enable
639         """
640         bridge = self._switches[switch_name]
641         return 'rstp_enable         : true' in ''.join(bridge.bridge_info())
642
643     def validate_disable_stp(self, _dummy_result, switch_name):
644         """ Validate stp disable
645         """
646         bridge = self._switches[switch_name]
647         return 'stp_enable          : false' in ''.join(bridge.bridge_info())
648
649     def validate_enable_stp(self, _dummy_result, switch_name):
650         """ Validate stp enable
651         """
652         bridge = self._switches[switch_name]
653         return 'stp_enable          : true' in ''.join(bridge.bridge_info())
654
655     def validate_restart(self, _dummy_result):
656         """ Validate restart
657         """
658         return True