1 # Copyright 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 """VSwitch controller for multi VM scenarios with serial or parallel connection
21 from core.vswitch_controller import IVswitchController
22 from vswitches.utils import add_ports_to_flow
23 from conf import settings
32 class VswitchControllerPXP(IVswitchController):
33 """VSwitch controller for PXP deployment scenario.
35 def __init__(self, deployment, vswitch_class, traffic):
36 """Initializes up the prerequisites for the PXP deployment scenario.
38 :vswitch_class: the vSwitch class to be used.
39 :deployment: the deployment scenario to configure
40 :traffic: dictionary with detailed traffic definition
42 self._logger = logging.getLogger(__name__)
43 self._vswitch_class = vswitch_class
44 self._vswitch = vswitch_class()
45 self._pxp_topology = 'parallel' if deployment.startswith('pvpv') else 'serial'
46 if deployment == 'pvp':
47 self._pxp_vm_count = 1
48 elif deployment.startswith('pvvp') or deployment.startswith('pvpv'):
49 if len(deployment) > 4:
50 self._pxp_vm_count = int(deployment[4:])
52 self._pxp_vm_count = 2
54 raise RuntimeError('Unknown number of VMs involved in {} deployment.'.format(deployment))
56 self._deployment_scenario = deployment
58 self._traffic = traffic.copy()
59 self._bidir = True if self._traffic['bidir'] == 'True' else False
60 self._logger.debug('Creation using ' + str(self._vswitch_class))
61 self._bridge = settings.getValue('VSWITCH_BRIDGE_NAME')
64 """ Sets up the switch for PXP
66 self._logger.debug('Setup using ' + str(self._vswitch_class))
71 self._vswitch.add_switch(self._bridge)
73 # create physical ports
74 (_, phy1_number) = self._vswitch.add_phy_port(self._bridge)
75 (_, phy2_number) = self._vswitch.add_phy_port(self._bridge)
78 # initialize vport array to requested number of VMs
79 guest_nics = settings.getValue('GUEST_NICS_NR')
80 vm_ports = [[] for _ in range(self._pxp_vm_count)]
81 # create as many VM ports as requested by configuration, but configure
82 # only even number of NICs or just one
83 for vmindex in range(self._pxp_vm_count):
84 # just for case, enforce even number of NICs or 1
85 nics_nr = int(guest_nics[vmindex] / 2) * 2 if guest_nics[vmindex] > 1 else 1
86 self._logger.debug('Create %s vports for %s. VM with index %s',
87 nics_nr, vmindex + 1, vmindex)
88 for _ in range(nics_nr):
89 (_, vport) = self._vswitch.add_vport(self._bridge)
90 vm_ports[vmindex].append(vport)
92 self._vswitch.del_flow(self._bridge)
94 # configure flows according to the TC definition
95 if self._pxp_topology == 'serial':
96 flow = _FLOW_TEMPLATE.copy()
97 if self._traffic['flow_type'] == 'IP':
98 flow.update({'dl_type':'0x0800',
99 'nw_src':self._traffic['l3']['srcip'],
100 'nw_dst':self._traffic['l3']['dstip']})
102 # insert flows for phy ports first
103 # from 1st PHY to 1st vport of 1st VM
108 # from last vport of last VM to 2nd phy
110 vm_ports[self._pxp_vm_count-1][-1],
114 # add serial connections among VMs and VM NICs pairs if needed
115 # in case of multiple NICs pairs per VM, the pairs are chained
116 # first, before flow to the next VM is created
117 for vmindex in range(self._pxp_vm_count):
118 # connect VMs NICs pairs in case of 4 and more NICs per VM
119 connections = [(vm_ports[vmindex][2*(x+1)-1],
120 vm_ports[vmindex][2*(x+1)])
121 for x in range(int(len(vm_ports[vmindex])/2)-1)]
122 for connection in connections:
127 # connect last NICs to the next VM if there is any
128 if self._pxp_vm_count > vmindex + 1:
130 vm_ports[vmindex][-1],
131 vm_ports[vmindex+1][0],
134 proto = _PROTO_TCP if self._traffic['l3']['proto'].lower() == 'tcp' else _PROTO_UDP
135 dst_mac_value = netaddr.EUI(self._traffic['l2']['dstmac']).value
136 dst_ip_value = netaddr.IPAddress(self._traffic['l3']['dstip']).value
137 # initialize stream index; every NIC pair of every VM uses unique stream
139 for vmindex in range(self._pxp_vm_count):
140 # iterate through all VMs NIC pairs...
141 if len(vm_ports[vmindex]) > 1:
142 port_pairs = [(vm_ports[vmindex][2*x],
143 vm_ports[vmindex][2*x+1]) for x in range(int(len(vm_ports[vmindex])/2))]
145 # ...or connect VM with just one NIC to both phy ports
146 port_pairs = [(vm_ports[vmindex][0], vm_ports[vmindex][0])]
148 for port_pair in port_pairs:
149 flow_p = _FLOW_TEMPLATE.copy()
150 flow_v = _FLOW_TEMPLATE.copy()
152 # update flow based on trafficgen settings
153 if self._traffic['stream_type'] == 'L2':
154 tmp_mac = netaddr.EUI(dst_mac_value + stream)
155 tmp_mac.dialect = netaddr.mac_unix_expanded
156 flow_p.update({'dl_dst':tmp_mac})
157 elif self._traffic['stream_type'] == 'L3':
158 tmp_ip = netaddr.IPAddress(dst_ip_value + stream)
159 flow_p.update({'dl_type':'0x800', 'nw_dst':tmp_ip})
160 elif self._traffic['stream_type'] == 'L4':
161 flow_p.update({'dl_type':'0x800', 'nw_proto':proto, 'tp_dst':stream})
163 raise RuntimeError('Unknown stream_type {}'.format(self._traffic['stream_type']))
165 # insert flow to dispatch traffic from physical ports
166 # to VMs based on stream type; all traffic from VMs is
167 # sent to physical ports to avoid issues with MAC swapping
168 # and upper layer mods performed inside guests
169 self._add_flow(flow_p, phy1_number, port_pair[0])
170 self._add_flow(flow_v, port_pair[1], phy2_number)
172 self._add_flow(flow_p, phy2_number, port_pair[1])
173 self._add_flow(flow_v, port_pair[0], phy1_number)
175 # every NIC pair needs its own unique traffic stream
183 """Tears down the switch created in setup().
185 self._logger.debug('Stop using ' + str(self._vswitch_class))
188 def _add_flow(self, flow, port1, port2, reverse_flow=False):
189 """ Helper method to insert flow into the vSwitch
191 self._vswitch.add_flow(self._bridge,
192 add_ports_to_flow(flow,
196 self._vswitch.add_flow(self._bridge,
197 add_ports_to_flow(flow,
204 def __exit__(self, type_, value, traceback):
207 def get_vswitch(self):
208 """See IVswitchController for description
212 def get_ports_info(self):
213 """See IVswitchController for description
215 self._logger.debug('get_ports_info using ' + str(self._vswitch_class))
216 return self._vswitch.get_ports(self._bridge)
218 def dump_vswitch_flows(self):
219 """See IVswitchController for description
221 self._vswitch.dump_flows(self._bridge)