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