1 # Copyright 2015-2016 Intel Corporation.
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 """VSPERF Open vSwitch base class
23 from conf import settings
24 from vswitches.vswitch import IVSwitch
25 from src.ovs import OFBridge, flow_key, flow_match
26 from tools import tasks
28 _OVS_VAR_DIR = settings.getValue('OVS_VAR_DIR')
29 _OVS_ETC_DIR = settings.getValue('OVS_ETC_DIR')
31 class IVSwitchOvs(IVSwitch, tasks.Process):
32 """Open vSwitch base class implementation
34 The method docstrings document only considerations specific to this
35 implementation. For generic information of the nature of the methods,
38 _logfile = os.path.join(settings.getValue('LOG_DIR'), settings.getValue('LOG_FILE_VSWITCHD'))
39 _ovsdb_pidfile_path = os.path.join(settings.getValue('LOG_DIR'), "ovsdb_pidfile.pid")
40 _vswitchd_pidfile_path = os.path.join(settings.getValue('LOG_DIR'), "vswitchd_pidfile.pid")
41 _proc_name = 'ovs-vswitchd'
44 """See IVswitch for general description
46 self._logger = logging.getLogger(__name__)
50 self._vswitchd_args = ['--pidfile=' + self._vswitchd_pidfile_path,
51 '--overwrite-pidfile', '--log-file=' + self._logfile]
53 self._cmd_template = ['sudo', '-E', os.path.join(settings.getValue('OVS_DIR'),
54 'vswitchd', 'ovs-vswitchd')]
57 """ Start ``ovsdb-server`` and ``ovs-vswitchd`` instance.
59 :raises: pexpect.EOF, pexpect.TIMEOUT
61 self._logger.info("Starting vswitchd...")
63 self._cmd = self._cmd_template + self._vswitchd_args
65 # DB must be started before vswitchd
69 # DB must be up before vswitchd config is altered or vswitchd started
75 tasks.Process.start(self)
77 except (pexpect.EOF, pexpect.TIMEOUT) as exc:
78 logging.error("Exception during VSwitch start.")
82 self._logger.info("Vswitchd...Started.")
85 """ Configure vswitchd through ovsdb if needed
90 """See IVswitch for general description
92 self._logger.info("Terminating vswitchd...")
94 self._logger.info("Vswitchd...Terminated.")
96 def add_switch(self, switch_name, params=None):
97 """See IVswitch for general description
99 bridge = OFBridge(switch_name)
100 bridge.create(params)
101 bridge.set_db_attribute('Open_vSwitch', '.',
102 'other_config:max-idle',
103 settings.getValue('VSWITCH_FLOW_TIMEOUT'))
104 self._bridges[switch_name] = bridge
106 def del_switch(self, switch_name):
107 """See IVswitch for general description
109 bridge = self._bridges[switch_name]
110 self._bridges.pop(switch_name)
113 def add_phy_port(self, switch_name):
114 """See IVswitch for general description
116 raise NotImplementedError
118 def add_vport(self, switch_name):
119 """See IVswitch for general description
121 raise NotImplementedError
123 def add_tunnel_port(self, switch_name, remote_ip, tunnel_type='vxlan', params=None):
124 """Creates tunneling port
126 bridge = self._bridges[switch_name]
127 pcount = str(self._get_port_count('type=' + tunnel_type))
128 port_name = tunnel_type + pcount
129 local_params = ['--', 'set', 'Interface', port_name,
130 'type=' + tunnel_type,
131 'options:remote_ip=' + remote_ip]
133 if params is not None:
134 local_params = local_params + params
136 of_port = bridge.add_port(port_name, local_params)
137 return (port_name, of_port)
139 def get_ports(self, switch_name):
140 """See IVswitch for general description
142 bridge = self._bridges[switch_name]
143 ports = list(bridge.get_ports().items())
144 return [(name, of_port) for (name, (of_port, _)) in ports]
146 def del_port(self, switch_name, port_name):
147 """See IVswitch for general description
149 bridge = self._bridges[switch_name]
150 bridge.del_port(port_name)
152 def add_flow(self, switch_name, flow, cache='off'):
153 """See IVswitch for general description
155 bridge = self._bridges[switch_name]
156 bridge.add_flow(flow, cache=cache)
158 def del_flow(self, switch_name, flow=None):
159 """See IVswitch for general description
162 bridge = self._bridges[switch_name]
163 bridge.del_flow(flow)
165 def dump_flows(self, switch_name):
166 """See IVswitch for general description
168 bridge = self._bridges[switch_name]
171 def add_route(self, switch_name, network, destination):
172 """See IVswitch for general description
174 bridge = self._bridges[switch_name]
175 bridge.add_route(network, destination)
177 def set_tunnel_arp(self, ip_addr, mac_addr, switch_name):
178 """See IVswitch for general description
180 bridge = self._bridges[switch_name]
181 bridge.set_tunnel_arp(ip_addr, mac_addr, switch_name)
183 def _get_port_count(self, param):
184 """Returns the number of ports having a certain parameter
187 for k in self._bridges:
188 pparams = [c for (_, (_, c)) in list(self._bridges[k].get_ports().items())]
189 phits = [i for i in pparams if param in i]
196 def kill(self, signal='-15', sleep=10):
197 """Kill ``ovs-vswitchd`` and ``ovs-ovsdb`` instances if they are alive.
201 if os.path.isfile(self._vswitchd_pidfile_path):
202 self._logger.info('Killing ovs-vswitchd...')
203 with open(self._vswitchd_pidfile_path, "r") as pidfile:
204 vswitchd_pid = pidfile.read().strip()
205 tasks.terminate_task(vswitchd_pid, logger=self._logger)
207 self._kill_ovsdb() # ovsdb must be killed after vswitchd
209 # just for case, that sudo envelope has not been terminated yet
210 tasks.Process.kill(self, signal, sleep)
214 def _reset_ovsdb(self):
215 """Reset system for 'ovsdb'.
219 self._logger.info('Resetting system after last run...')
221 tasks.run_task(['sudo', 'rm', '-rf', _OVS_VAR_DIR], self._logger)
222 tasks.run_task(['sudo', 'mkdir', '-p', _OVS_VAR_DIR], self._logger)
223 tasks.run_task(['sudo', 'rm', '-rf', _OVS_ETC_DIR], self._logger)
224 tasks.run_task(['sudo', 'mkdir', '-p', _OVS_ETC_DIR], self._logger)
226 tasks.run_task(['sudo', 'rm', '-f',
227 os.path.join(_OVS_ETC_DIR, 'conf.db')],
230 self._logger.info('System reset after last run.')
232 def _start_ovsdb(self):
233 """Start ``ovsdb-server`` instance.
237 ovsdb_tool_bin = os.path.join(
238 settings.getValue('OVS_DIR'), 'ovsdb', 'ovsdb-tool')
239 tasks.run_task(['sudo', ovsdb_tool_bin, 'create',
240 os.path.join(_OVS_ETC_DIR, 'conf.db'),
241 os.path.join(settings.getValue('OVS_DIR'), 'vswitchd',
242 'vswitch.ovsschema')],
244 'Creating ovsdb configuration database...')
246 ovsdb_server_bin = os.path.join(
247 settings.getValue('OVS_DIR'), 'ovsdb', 'ovsdb-server')
249 tasks.run_background_task(
250 ['sudo', ovsdb_server_bin,
251 '--remote=punix:%s' % os.path.join(_OVS_VAR_DIR, 'db.sock'),
252 '--remote=db:Open_vSwitch,Open_vSwitch,manager_options',
253 '--pidfile=' + self._ovsdb_pidfile_path, '--overwrite-pidfile'],
255 'Starting ovsdb-server...')
257 def _kill_ovsdb(self):
258 """Kill ``ovsdb-server`` instance.
262 if os.path.isfile(self._ovsdb_pidfile_path):
263 with open(self._ovsdb_pidfile_path, "r") as pidfile:
264 ovsdb_pid = pidfile.read().strip()
266 self._logger.info("Killing ovsdb with pid: " + ovsdb_pid)
269 tasks.terminate_task(ovsdb_pid, logger=self._logger)
272 def get_db_sock_path():
273 """Method returns location of db.sock file
275 :returns: path to db.sock file.
277 return os.path.join(_OVS_VAR_DIR, 'db.sock')
280 # validate methods required for integration testcases
283 def validate_add_switch(self, result, switch_name, params=None):
284 """Validate - Create a new logical switch with no ports
286 bridge = self._bridges[switch_name]
287 output = bridge.run_vsctl(['show'], check_error=True)
288 assert not output[1] # there shouldn't be any stderr, but in case
289 assert re.search('Bridge ["\']?%s["\']?' % switch_name, output[0]) is not None
292 def validate_del_switch(self, result, switch_name):
293 """Validate removal of switch
295 bridge = OFBridge('tmp')
296 output = bridge.run_vsctl(['show'], check_error=True)
297 assert not output[1] # there shouldn't be any stderr, but in case
298 assert re.search('Bridge ["\']?%s["\']?' % switch_name, output[0]) is None
301 def validate_add_phy_port(self, result, switch_name):
302 """ Validate that physical port was added to bridge.
304 bridge = self._bridges[switch_name]
305 output = bridge.run_vsctl(['show'], check_error=True)
306 assert not output[1] # there shouldn't be any stderr, but in case
307 assert re.search('Port ["\']?%s["\']?' % result[0], output[0]) is not None
308 assert re.search('Interface ["\']?%s["\']?' % result[0], output[0]) is not None
311 def validate_add_vport(self, result, switch_name):
312 """ Validate that virtual port was added to bridge.
314 return self.validate_add_phy_port(result, switch_name)
316 def validate_del_port(self, result, switch_name, port_name):
317 """ Validate that port_name was removed from bridge.
319 bridge = self._bridges[switch_name]
320 output = bridge.run_vsctl(['show'], check_error=True)
321 assert not output[1] # there shouldn't be any stderr, but in case
322 assert 'Port "%s"' % port_name not in output[0]
325 def validate_add_flow(self, result, switch_name, flow, cache='off'):
326 """ Validate insertion of the flow into the switch
328 if 'idle_timeout' in flow:
329 del(flow['idle_timeout'])
331 # Note: it should be possible to call `ovs-ofctl dump-flows switch flow`
332 # to verify flow insertion, but it doesn't accept the same flow syntax
333 # as add-flow, so we have to compare it the hard way
335 # get dump of flows and compare them one by one
336 flow_src = flow_key(flow)
337 bridge = self._bridges[switch_name]
338 output = bridge.run_ofctl(['dump-flows', switch_name], check_error=True)
339 for flow_dump in output[0].split('\n'):
340 if flow_match(flow_dump, flow_src):
341 # flow was added correctly
345 def validate_del_flow(self, result, switch_name, flow=None):
346 """ Validate removal of the flow
349 # what else we can do?
351 return not self.validate_add_flow(result, switch_name, flow)
353 def validate_dump_flows(self, result, switch_name):
354 """ Validate call of flow dump