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
22 from conf import settings
23 from vswitches.vswitch import IVSwitch
24 from src.ovs import OFBridge, flow_key, flow_match
25 from tools import tasks
27 _OVS_VAR_DIR = settings.getValue('OVS_VAR_DIR')
28 _OVS_ETC_DIR = settings.getValue('OVS_ETC_DIR')
30 class IVSwitchOvs(IVSwitch, tasks.Process):
31 """Open vSwitch base class implementation
33 The method docstrings document only considerations specific to this
34 implementation. For generic information of the nature of the methods,
37 _logfile = os.path.join(settings.getValue('LOG_DIR'), settings.getValue('LOG_FILE_VSWITCHD'))
38 _ovsdb_pidfile_path = os.path.join(settings.getValue('LOG_DIR'), "ovsdb_pidfile.pid")
39 _vswitchd_pidfile_path = os.path.join(settings.getValue('LOG_DIR'), "vswitchd_pidfile.pid")
40 _proc_name = 'ovs-vswitchd'
43 """See IVswitch for general description
45 self._logger = logging.getLogger(__name__)
49 self._vswitchd_args = ['--pidfile=' + self._vswitchd_pidfile_path,
50 '--overwrite-pidfile', '--log-file=' + self._logfile]
52 self._cmd_template = ['sudo', '-E', os.path.join(settings.getValue('OVS_DIR'),
53 'vswitchd', 'ovs-vswitchd')]
56 """ Start ``ovsdb-server`` and ``ovs-vswitchd`` instance.
58 :raises: pexpect.EOF, pexpect.TIMEOUT
60 self._logger.info("Starting vswitchd...")
62 self._cmd = self._cmd_template + self._vswitchd_args
64 # DB must be started before vswitchd
68 # DB must be up before vswitchd config is altered
72 tasks.Process.start(self)
74 except (pexpect.EOF, pexpect.TIMEOUT) as exc:
75 logging.error("Exception during VSwitch start.")
79 self._logger.info("Vswitchd...Started.")
82 """ Configure vswitchd through ovsdb if needed
87 """See IVswitch for general description
89 self._logger.info("Terminating vswitchd...")
91 self._logger.info("Vswitchd...Terminated.")
93 def add_switch(self, switch_name, params=None):
94 """See IVswitch for general description
96 bridge = OFBridge(switch_name)
98 bridge.set_db_attribute('Open_vSwitch', '.',
99 'other_config:max-idle',
100 settings.getValue('VSWITCH_FLOW_TIMEOUT'))
101 self._bridges[switch_name] = bridge
103 def del_switch(self, switch_name):
104 """See IVswitch for general description
106 bridge = self._bridges[switch_name]
107 self._bridges.pop(switch_name)
110 def add_phy_port(self, switch_name):
111 """See IVswitch for general description
113 raise NotImplementedError
115 def add_vport(self, switch_name):
116 """See IVswitch for general description
118 raise NotImplementedError
120 def add_tunnel_port(self, switch_name, remote_ip, tunnel_type='vxlan', params=None):
121 """Creates tunneling port
123 bridge = self._bridges[switch_name]
124 pcount = str(self._get_port_count('type=' + tunnel_type))
125 port_name = tunnel_type + pcount
126 local_params = ['--', 'set', 'Interface', port_name,
127 'type=' + tunnel_type,
128 'options:remote_ip=' + remote_ip]
130 if params is not None:
131 local_params = local_params + params
133 of_port = bridge.add_port(port_name, local_params)
134 return (port_name, of_port)
136 def get_ports(self, switch_name):
137 """See IVswitch for general description
139 bridge = self._bridges[switch_name]
140 ports = list(bridge.get_ports().items())
141 return [(name, of_port) for (name, (of_port, _)) in ports]
143 def del_port(self, switch_name, port_name):
144 """See IVswitch for general description
146 bridge = self._bridges[switch_name]
147 bridge.del_port(port_name)
149 def add_flow(self, switch_name, flow, cache='off'):
150 """See IVswitch for general description
152 bridge = self._bridges[switch_name]
153 bridge.add_flow(flow, cache=cache)
155 def del_flow(self, switch_name, flow=None):
156 """See IVswitch for general description
159 bridge = self._bridges[switch_name]
160 bridge.del_flow(flow)
162 def dump_flows(self, switch_name):
163 """See IVswitch for general description
165 bridge = self._bridges[switch_name]
168 def add_route(self, switch_name, network, destination):
169 """See IVswitch for general description
171 bridge = self._bridges[switch_name]
172 bridge.add_route(network, destination)
174 def set_tunnel_arp(self, ip_addr, mac_addr, switch_name):
175 """See IVswitch for general description
177 bridge = self._bridges[switch_name]
178 bridge.set_tunnel_arp(ip_addr, mac_addr, switch_name)
180 def _get_port_count(self, param):
181 """Returns the number of ports having a certain parameter
184 for k in self._bridges:
185 pparams = [c for (_, (_, c)) in list(self._bridges[k].get_ports().items())]
186 phits = [i for i in pparams if param in i]
193 def kill(self, signal='-15', sleep=10):
194 """Kill ``ovs-vswitchd`` and ``ovs-ovsdb`` instances if they are alive.
198 if os.path.isfile(self._vswitchd_pidfile_path):
199 self._logger.info('Killing ovs-vswitchd...')
200 with open(self._vswitchd_pidfile_path, "r") as pidfile:
201 vswitchd_pid = pidfile.read().strip()
202 tasks.terminate_task(vswitchd_pid, logger=self._logger)
204 self._kill_ovsdb() # ovsdb must be killed after vswitchd
206 # just for case, that sudo envelope has not been terminated yet
207 tasks.Process.kill(self, signal, sleep)
211 def _reset_ovsdb(self):
212 """Reset system for 'ovsdb'.
216 self._logger.info('Resetting system after last run...')
218 tasks.run_task(['sudo', 'rm', '-rf', _OVS_VAR_DIR], self._logger)
219 tasks.run_task(['sudo', 'mkdir', '-p', _OVS_VAR_DIR], self._logger)
220 tasks.run_task(['sudo', 'rm', '-rf', _OVS_ETC_DIR], self._logger)
221 tasks.run_task(['sudo', 'mkdir', '-p', _OVS_ETC_DIR], self._logger)
223 tasks.run_task(['sudo', 'rm', '-f',
224 os.path.join(_OVS_ETC_DIR, 'conf.db')],
227 self._logger.info('System reset after last run.')
229 def _start_ovsdb(self):
230 """Start ``ovsdb-server`` instance.
234 ovsdb_tool_bin = os.path.join(
235 settings.getValue('OVS_DIR'), 'ovsdb', 'ovsdb-tool')
236 tasks.run_task(['sudo', ovsdb_tool_bin, 'create',
237 os.path.join(_OVS_ETC_DIR, 'conf.db'),
238 os.path.join(settings.getValue('OVS_DIR'), 'vswitchd',
239 'vswitch.ovsschema')],
241 'Creating ovsdb configuration database...')
243 ovsdb_server_bin = os.path.join(
244 settings.getValue('OVS_DIR'), 'ovsdb', 'ovsdb-server')
246 tasks.run_background_task(
247 ['sudo', ovsdb_server_bin,
248 '--remote=punix:%s' % os.path.join(_OVS_VAR_DIR, 'db.sock'),
249 '--remote=db:Open_vSwitch,Open_vSwitch,manager_options',
250 '--pidfile=' + self._ovsdb_pidfile_path, '--overwrite-pidfile'],
252 'Starting ovsdb-server...')
254 def _kill_ovsdb(self):
255 """Kill ``ovsdb-server`` instance.
259 if os.path.isfile(self._ovsdb_pidfile_path):
260 with open(self._ovsdb_pidfile_path, "r") as pidfile:
261 ovsdb_pid = pidfile.read().strip()
263 self._logger.info("Killing ovsdb with pid: " + ovsdb_pid)
266 tasks.terminate_task(ovsdb_pid, logger=self._logger)
269 def get_db_sock_path():
270 """Method returns location of db.sock file
272 :returns: path to db.sock file.
274 return os.path.join(_OVS_VAR_DIR, 'db.sock')
277 # validate methods required for integration testcases
280 def validate_add_switch(self, result, switch_name, params=None):
281 """Validate - Create a new logical switch with no ports
283 bridge = self._bridges[switch_name]
284 output = bridge.run_vsctl(['show'], check_error=True)
285 assert not output[1] # there shouldn't be any stderr, but in case
286 assert re.search('Bridge ["\']?%s["\']?' % switch_name, output[0]) is not None
289 def validate_del_switch(self, result, switch_name):
290 """Validate removal of switch
292 bridge = OFBridge('tmp')
293 output = bridge.run_vsctl(['show'], check_error=True)
294 assert not output[1] # there shouldn't be any stderr, but in case
295 assert re.search('Bridge ["\']?%s["\']?' % switch_name, output[0]) is None
298 def validate_add_phy_port(self, result, switch_name):
299 """ Validate that physical port was added to bridge.
301 bridge = self._bridges[switch_name]
302 output = bridge.run_vsctl(['show'], check_error=True)
303 assert not output[1] # there shouldn't be any stderr, but in case
304 assert re.search('Port ["\']?%s["\']?' % result[0], output[0]) is not None
305 assert re.search('Interface ["\']?%s["\']?' % result[0], output[0]) is not None
308 def validate_add_vport(self, result, switch_name):
309 """ Validate that virtual port was added to bridge.
311 return self.validate_add_phy_port(result, switch_name)
313 def validate_del_port(self, result, switch_name, port_name):
314 """ Validate that port_name was removed from bridge.
316 bridge = self._bridges[switch_name]
317 output = bridge.run_vsctl(['show'], check_error=True)
318 assert not output[1] # there shouldn't be any stderr, but in case
319 assert 'Port "%s"' % port_name not in output[0]
322 def validate_add_flow(self, result, switch_name, flow, cache='off'):
323 """ Validate insertion of the flow into the switch
325 if 'idle_timeout' in flow:
326 del(flow['idle_timeout'])
328 # Note: it should be possible to call `ovs-ofctl dump-flows switch flow`
329 # to verify flow insertion, but it doesn't accept the same flow syntax
330 # as add-flow, so we have to compare it the hard way
332 # get dump of flows and compare them one by one
333 flow_src = flow_key(flow)
334 bridge = self._bridges[switch_name]
335 output = bridge.run_ofctl(['dump-flows', switch_name], check_error=True)
336 for flow_dump in output[0].split('\n'):
337 if flow_match(flow_dump, flow_src):
338 # flow was added correctly
342 def validate_del_flow(self, result, switch_name, flow=None):
343 """ Validate removal of the flow
346 # what else we can do?
348 return not self.validate_add_flow(result, switch_name, flow)
350 def validate_dump_flows(self, result, switch_name):
351 """ Validate call of flow dump