Upload the contribution of vstf as bottleneck network framework.
[bottlenecks.git] / vstf / vstf / agent / env / vswitch_plugins / ovs_plugin.py
1 """
2 Created on 2015-10-10
3
4 @author: y00228926
5 """
6 import os
7 import shutil
8 import logging
9 import time
10 import re
11
12 from vstf.agent.env.vswitch_plugins import model
13 from vstf.common.utils import check_and_kill, check_and_rmmod, check_call, check_output, \
14     get_eth_by_bdf, my_mkdir, call
15
16 LOG = logging.getLogger(__name__)
17
18
19 class OvsPlugin(model.VswitchPlugin):
20
21     def __init__(self):
22         self.daemons = ['ovs-vswitchd', 'ovsdb-server']
23         self.mods = ['openvswitch']
24         self.dirs = {'db': "/usr/local/etc/openvswitch"}
25         self.cmds = []
26         self.cmds.append("mkdir -p /usr/local/etc/openvswitch")
27         self.cmds.append("ovsdb-tool create /usr/local/etc/openvswitch/conf.db")
28         self.cmds.append("ovsdb-server --remote=punix:/usr/local/var/run/openvswitch/db.sock \
29              --remote=db:Open_vSwitch,Open_vSwitch,manager_options \
30              --private-key=db:Open_vSwitch,SSL,private_key \
31              --certificate=db:Open_vSwitch,SSL,certificate \
32              --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert \
33              --pidfile --detach")
34         self.cmds.append("ovs-vsctl --no-wait init")
35         self.cmds.append("ovs-vswitchd --pidfile --detach")
36         self.initialized = False
37
38     def init(self):
39         if not self.initialized:
40             self._start_servers()
41             self.initialized = True
42
43     def clean(self):
44         """clean for ovs. Rmmod openvswitch.ko, kill openvswitch daemon process.
45
46         """
47         for process in self.daemons:
48             check_and_kill(process)
49         for mod in self.mods:
50             check_and_rmmod(mod)
51         for _, directory in self.dirs.items():
52             if os.path.isdir(directory):
53                 LOG.info('rm -rf %s', directory)
54                 shutil.rmtree(directory, ignore_errors=True)
55         self.initialized = False
56         return True
57
58     def create_br(self, br_cfg):
59         """Create a bridge(virtual switch). Return True for success, return False for failure.
60
61         :param dict    br_cfg: configuration for bridge creation like
62                 {
63                     "type": "ovs",
64                     "name": "ovs1",
65                     "uplinks": [
66                         {
67                             "bdf": "04:00.0",
68                             "vlan_mode": "access",
69                             "vlan_id": "1"
70                         }
71                     ],
72                     "vtep": {},
73                 }
74
75         """
76         self.init()
77         name, uplinks = br_cfg['name'], br_cfg['uplinks']
78
79         check_call("ovs-vsctl add-br %s" % (name), shell=True)
80         if br_cfg['vtep']: # vxlan supports
81             local_ip, remote_ip = br_cfg['vtep']['local_ip'], br_cfg['vtep']['remote_ip']
82             assert len(uplinks) == 1
83             uplink = uplinks[0]
84             device = get_eth_by_bdf(uplink['bdf'])
85             time.sleep(0.5)
86             vtep = 'vx1'
87             check_call("ifconfig %s %s up" % (device, local_ip), shell=True)
88             check_call("ovs-vsctl add-port %s %s" % (name, vtep), shell=True)
89             check_call("ovs-vsctl set interface %s type=vxlan options:remote_ip=%s" % (vtep, remote_ip), shell=True)
90         for uplink in uplinks:
91             device = get_eth_by_bdf(uplink['bdf'])
92             vlan_mode = uplink['vlan_mode']
93             vlan_id = uplink['vlan_id']
94             check_call("ip link set dev %s up" % device, shell=True)
95             call("ethtool -A %s rx off tx off " % device, shell=True)
96             check_call("ovs-vsctl add-port %s %s" % (name, device), shell=True)
97             if vlan_mode == 'trunk':
98                 check_call("ovs-vsctl set port %s trunks=%s" % (device, vlan_id), shell=True)
99             elif vlan_mode == 'access':
100                 check_call("ovs-vsctl set port %s tag=%s" % (device, vlan_id), shell=True)
101             else:
102                 raise Exception("unreconized vlan_mode:%s" % vlan_mode)
103         return True
104
105     def set_tap_vid(self, tap_cfg):
106         """set vlan id or vxlan id for tap device(virtual nic for vm).
107         return True for success, return False for failure.
108
109         :param dict    tap_cfg: dictionary config for tap device like
110                         {
111                             "tap_name": "tap_in",
112                             "vlan_mode": "access",
113                             "vlan_id": "1"
114                         }
115
116         """
117         port, vlan_mode, vlan = tap_cfg['tap_name'], tap_cfg['vlan_mode'], tap_cfg['vlan_id']
118         assert vlan_mode in ('access', 'vxlan')
119         if int(vlan) > '4095':
120             # vxlan setting
121             self.__set_tap_vid(port, "vxlan", vlan)
122         else:
123             # vlan setting
124             self.__set_tap_vid(port, vlan_mode, vlan)
125         return True
126
127     def set_fastlink(self, br_cfg):
128         """connect two ports directly, so that packets comes from any one port be forwarded to the other.
129         return True for success, return False for failure.
130
131         :param dict    br_cfg: dictionary configuration for linking ports.
132                 {
133                     "name": "ovs1",
134                     "fastlink": [
135                         {
136                             "inport": "04:00.0",
137                             "outport": "tap_in"
138                         }
139                     ]
140                 }
141         """
142         br_name = br_cfg['name']
143         for fast_cfg in br_cfg['fastlink']:
144             p1, p2 = fast_cfg['inport'], fast_cfg['outport']
145         self.__fastlink(br_name, p1, p2)
146         return True
147
148     def _start_servers(self):
149         for _, directory in self.dirs.items():
150             my_mkdir(directory)
151         for mod in self.mods:
152             check_call("modprobe %s" % mod, shell=True)
153         for cmd in self.cmds:
154             check_call(cmd, shell=True)
155         return True
156
157     def __set_tap_vid(self, port, vlan_mode, vlan_id):
158         if vlan_mode == 'vxlan':
159             raise Exception("don't support vxlan setting right now.")
160         elif vlan_mode == 'trunk':
161             check_call("ovs-vsctl set port %s trunks=%s" % (port, vlan_id), shell=True)
162         else:
163             check_call("ovs-vsctl set port %s tag=%s" % (port, vlan_id), shell=True)
164
165     def __fastlink(self, br, p1, p2):
166         LOG.info("_fastlink(%s,%s,%s)", br, p1, p2)
167         p1 = p1.replace(' ', '')
168         p2 = p2.replace(' ', '')
169         bdfs = check_output("lspci |grep Eth | awk '{print $1}'", shell=True).splitlines()
170         if p1 in bdfs:
171             p1 = get_eth_by_bdf(p1)
172         if p2 in bdfs:
173             p2 = get_eth_by_bdf(p2)
174         ovs_port = {}
175         buf = check_output("ovs-ofctl show %s" % br, shell=True)
176         port_info = re.compile(r"[0-9]+\(.*\)", re.IGNORECASE | re.MULTILINE)
177         for s in port_info.findall(buf):
178             port_num, interface = s.replace('(', ' ').replace(')', ' ').split()
179             ovs_port[interface] = port_num
180         pn1, pn2 = ovs_port[p1], ovs_port[p2]
181         check_call("ovs-ofctl add-flow %s in_port=%s,priority=100,action=output:%s" % (br, pn1, pn2), shell=True)
182         check_call("ovs-ofctl add-flow %s in_port=%s,priority=100,action=output:%s" % (br, pn2, pn1), shell=True)
183         return True