summaryrefslogtreecommitdiffstats
path: root/vstf/vstf/agent/env/vswitch_plugins/ovs_plugin.py
blob: 460459504de41c99753cd175a1cd0a56315bcc68 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
"""
Created on 2015-10-10

@author: y00228926
"""
import os
import shutil
import logging
import time
import re

from vstf.agent.env.vswitch_plugins import model
from vstf.common.utils import check_and_kill, check_and_rmmod, check_call, check_output, \
    get_eth_by_bdf, my_mkdir, call

LOG = logging.getLogger(__name__)


class OvsPlugin(model.VswitchPlugin):

    def __init__(self):
        self.daemons = ['ovs-vswitchd', 'ovsdb-server']
        self.mods = ['openvswitch']
        self.dirs = {'db': "/usr/local/etc/openvswitch"}
        self.cmds = []
        self.cmds.append("mkdir -p /usr/local/etc/openvswitch")
        self.cmds.append("ovsdb-tool create /usr/local/etc/openvswitch/conf.db")
        self.cmds.append("ovsdb-server --remote=punix:/usr/local/var/run/openvswitch/db.sock \
             --remote=db:Open_vSwitch,Open_vSwitch,manager_options \
             --private-key=db:Open_vSwitch,SSL,private_key \
             --certificate=db:Open_vSwitch,SSL,certificate \
             --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert \
             --pidfile --detach")
        self.cmds.append("ovs-vsctl --no-wait init")
        self.cmds.append("ovs-vswitchd --pidfile --detach")
        self.initialized = False

    def init(self):
        if not self.initialized:
            self._start_servers()
            self.initialized = True

    def clean(self):
        """clean for ovs. Rmmod openvswitch.ko, kill openvswitch daemon process.

        """
        for process in self.daemons:
            check_and_kill(process)
        for mod in self.mods:
            check_and_rmmod(mod)
        for _, directory in self.dirs.items():
            if os.path.isdir(directory):
                LOG.info('rm -rf %s', directory)
                shutil.rmtree(directory, ignore_errors=True)
        self.initialized = False
        return True

    def create_br(self, br_cfg):
        """Create a bridge(virtual switch). Return True for success, return False for failure.

        :param dict    br_cfg: configuration for bridge creation like
                {
                    "type": "ovs",
                    "name": "ovs1",
                    "uplinks": [
                        {
                            "bdf": "04:00.0",
                            "vlan_mode": "access",
                            "vlan_id": "1"
                        }
                    ],
                    "vtep": {},
                }

        """
        self.init()
        name, uplinks = br_cfg['name'], br_cfg['uplinks']

        check_call("ovs-vsctl add-br %s" % (name), shell=True)
        if br_cfg['vtep']: # vxlan supports
            local_ip, remote_ip = br_cfg['vtep']['local_ip'], br_cfg['vtep']['remote_ip']
            assert len(uplinks) == 1
            uplink = uplinks[0]
            device = get_eth_by_bdf(uplink['bdf'])
            time.sleep(0.5)
            vtep = 'vx1'
            check_call("ifconfig %s %s up" % (device, local_ip), shell=True)
            check_call("ovs-vsctl add-port %s %s" % (name, vtep), shell=True)
            check_call("ovs-vsctl set interface %s type=vxlan options:remote_ip=%s" % (vtep, remote_ip), shell=True)
        for uplink in uplinks:
            device = get_eth_by_bdf(uplink['bdf'])
            vlan_mode = uplink['vlan_mode']
            vlan_id = uplink['vlan_id']
            check_call("ip link set dev %s up" % device, shell=True)
            call("ethtool -A %s rx off tx off " % device, shell=True)
            check_call("ovs-vsctl add-port %s %s" % (name, device), shell=True)
            if vlan_mode == 'trunk':
                check_call("ovs-vsctl set port %s trunks=%s" % (device, vlan_id), shell=True)
            elif vlan_mode == 'access':
                check_call("ovs-vsctl set port %s tag=%s" % (device, vlan_id), shell=True)
            else:
                raise Exception("unreconized vlan_mode:%s" % vlan_mode)
        return True

    def set_tap_vid(self, tap_cfg):
        """set vlan id or vxlan id for tap device(virtual nic for vm).
        return True for success, return False for failure.

        :param dict    tap_cfg: dictionary config for tap device like
                        {
                            "tap_name": "tap_in",
                            "vlan_mode": "access",
                            "vlan_id": "1"
                        }

        """
        port, vlan_mode, vlan = tap_cfg['tap_name'], tap_cfg['vlan_mode'], tap_cfg['vlan_id']
        assert vlan_mode in ('access', 'vxlan')
        if int(vlan) > '4095':
            # vxlan setting
            self.__set_tap_vid(port, "vxlan", vlan)
        else:
            # vlan setting
            self.__set_tap_vid(port, vlan_mode, vlan)
        return True

    def set_fastlink(self, br_cfg):
        """connect two ports directly, so that packets comes from any one port be forwarded to the other.
        return True for success, return False for failure.

        :param dict    br_cfg: dictionary configuration for linking ports.
                {
                    "name": "ovs1",
                    "fastlink": [
                        {
                            "inport": "04:00.0",
                            "outport": "tap_in"
                        }
                    ]
                }
        """
        br_name = br_cfg['name']
        for fast_cfg in br_cfg['fastlink']:
            p1, p2 = fast_cfg['inport'], fast_cfg['outport']
        self.__fastlink(br_name, p1, p2)
        return True

    def _start_servers(self):
        for _, directory in self.dirs.items():
            my_mkdir(directory)
        for mod in self.mods:
            check_call("modprobe %s" % mod, shell=True)
        for cmd in self.cmds:
            check_call(cmd, shell=True)
        return True

    def __set_tap_vid(self, port, vlan_mode, vlan_id):
        if vlan_mode == 'vxlan':
            raise Exception("don't support vxlan setting right now.")
        elif vlan_mode == 'trunk':
            check_call("ovs-vsctl set port %s trunks=%s" % (port, vlan_id), shell=True)
        else:
            check_call("ovs-vsctl set port %s tag=%s" % (port, vlan_id), shell=True)

    def __fastlink(self, br, p1, p2):
        LOG.info("_fastlink(%s,%s,%s)", br, p1, p2)
        p1 = p1.replace(' ', '')
        p2 = p2.replace(' ', '')
        bdfs = check_output("lspci |grep Eth | awk '{print $1}'", shell=True).splitlines()
        if p1 in bdfs:
            p1 = get_eth_by_bdf(p1)
        if p2 in bdfs:
            p2 = get_eth_by_bdf(p2)
        ovs_port = {}
        buf = check_output("ovs-ofctl show %s" % br, shell=True)
        port_info = re.compile(r"[0-9]+\(.*\)", re.IGNORECASE | re.MULTILINE)
        for s in port_info.findall(buf):
            port_num, interface = s.replace('(', ' ').replace(')', ' ').split()
            ovs_port[interface] = port_num
        pn1, pn2 = ovs_port[p1], ovs_port[p2]
        check_call("ovs-ofctl add-flow %s in_port=%s,priority=100,action=output:%s" % (br, pn1, pn2), shell=True)
        check_call("ovs-ofctl add-flow %s in_port=%s,priority=100,action=output:%s" % (br, pn2, pn1), shell=True)
        return True