0174d9c0dc521ee9f8e1900af6cdc5a188d3e1b0
[yardstick.git] / yardstick / network_services / helpers / samplevnf_helper.py
1 # Copyright (c) 2016-2017 Intel Corporation
2 #
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
6 #
7 #      http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 from __future__ import absolute_import
16
17 import ipaddress
18 import logging
19 import os
20 import sys
21 from collections import OrderedDict, defaultdict
22 from itertools import chain, repeat
23
24 import six
25 from six.moves.configparser import ConfigParser
26
27 from yardstick.common.utils import ip_to_hex
28
29 LOG = logging.getLogger(__name__)
30
31 LINK_CONFIG_TEMPLATE = """\
32 link {0} down
33 link {0} config {1} {2}
34 link {0} up
35 """
36
37 ACTION_TEMPLATE = """\
38 p action add {0} accept
39 p action add {0} fwd {0}
40 p action add {0} count
41 """
42
43 FW_ACTION_TEMPLATE = """\
44 p action add {0} accept
45 p action add {0} fwd {0}
46 p action add {0} count
47 p action add {0} conntrack
48 """
49
50 # This sets up a basic passthrough with no rules
51 SCRIPT_TPL = """
52 {link_config}
53
54 {arp_config}
55
56 {arp_config6}
57
58 {actions}
59
60 {rules}
61
62 """
63
64
65 class PortPairs(object):
66
67     DOWNLINK = "downlink"
68     UPLINK = "uplink"
69
70     def __init__(self, interfaces):
71         super(PortPairs, self).__init__()
72         self.interfaces = interfaces
73         self._all_ports = None
74         self._uplink_ports = None
75         self._downlink_ports = None
76         self._networks = None
77         self._port_pair_list = None
78         self._valid_networks = None
79
80     @property
81     def networks(self):
82         if self._networks is None:
83             self._networks = {}
84             for intf in self.interfaces:
85                 vintf = intf['virtual-interface']
86                 try:
87                     vld_id = vintf['vld_id']
88                 except KeyError:
89                     # probably unused port?
90                     LOG.warning("intf without vld_id, %s", vintf)
91                 else:
92                     self._networks.setdefault(vld_id, []).append(vintf["ifname"])
93         return self._networks
94
95     @classmethod
96     def get_downlink_id(cls, vld_id):
97         # partition returns a tuple
98         parts = list(vld_id.partition(cls.UPLINK))
99         if parts[0]:
100             # 'uplink' was not in or not leftmost in the string
101             return
102         parts[1] = cls.DOWNLINK
103         public_id = ''.join(parts)
104         return public_id
105
106     @property
107     # this only works for vnfs that have both uplink and public visible
108     def valid_networks(self):
109         if self._valid_networks is None:
110             self._valid_networks = []
111             for vld_id in self.networks:
112                 downlink_id = self.get_downlink_id(vld_id)
113                 if downlink_id in self.networks:
114                     self._valid_networks.append((vld_id, downlink_id))
115         return self._valid_networks
116
117     @property
118     def all_ports(self):
119         if self._all_ports is None:
120             self._all_ports = sorted(set(self.uplink_ports + self.downlink_ports))
121         return self._all_ports
122
123     @property
124     def uplink_ports(self):
125         if self._uplink_ports is None:
126             intfs = chain.from_iterable(
127                 intfs for vld_id, intfs in self.networks.items() if
128                 vld_id.startswith(self.UPLINK))
129             self._uplink_ports = sorted(set(intfs))
130         return self._uplink_ports
131
132     @property
133     def downlink_ports(self):
134         if self._downlink_ports is None:
135             intfs = chain.from_iterable(
136                 intfs for vld_id, intfs in self.networks.items() if
137                 vld_id.startswith(self.DOWNLINK))
138             self._downlink_ports = sorted(set(intfs))
139         return self._downlink_ports
140
141     @property
142     def port_pair_list(self):
143         if self._port_pair_list is None:
144             self._port_pair_list = []
145
146             for uplink, downlink in self.valid_networks:
147                 for uplink_intf in self.networks[uplink]:
148                     # only VNFs have uplink, public peers
149                     peer_intfs = self.networks.get(downlink, [])
150                     if peer_intfs:
151                         for downlink_intf in peer_intfs:
152                             port_pair = uplink_intf, downlink_intf
153                             self._port_pair_list.append(port_pair)
154         return self._port_pair_list
155
156
157 class MultiPortConfig(object):
158
159     HW_LB = "HW"
160
161     @staticmethod
162     def float_x_plus_one_tenth_of_y(x, y):
163         return float(x) + float(y) / 10.0
164
165     @staticmethod
166     def make_str(base, iterator):
167         return ' '.join((base.format(x) for x in iterator))
168
169     @classmethod
170     def make_range_str(cls, base, start, stop=0, offset=0):
171         if offset and not stop:
172             stop = start + offset
173         return cls.make_str(base, range(start, stop))
174
175     @staticmethod
176     def parser_get(parser, section, key, default=None):
177         if parser.has_option(section, key):
178             return parser.get(section, key)
179         return default
180
181     @staticmethod
182     def make_ip_addr(ip, mask):
183         """
184         :param ip: ip adddress
185         :type ip: str
186         :param mask: /24 prefix of 255.255.255.0 netmask
187         :type mask: str
188         :return: interface
189         :rtype: IPv4Interface
190         """
191
192         try:
193             return ipaddress.ip_interface(six.text_type('/'.join([ip, mask])))
194         except (TypeError, ValueError):
195             # None so we can skip later
196             return None
197
198     @classmethod
199     def validate_ip_and_prefixlen(cls, ip_addr, prefixlen):
200         ip_addr = cls.make_ip_addr(ip_addr, prefixlen)
201         return ip_addr.ip.exploded, ip_addr.network.prefixlen
202
203     def __init__(self, topology_file, config_tpl, tmp_file, vnfd_helper,
204                  vnf_type='CGNAT', lb_count=2, worker_threads=3,
205                  worker_config='1C/1T', lb_config='SW', socket=0):
206
207         super(MultiPortConfig, self).__init__()
208         self.topology_file = topology_file
209         self.worker_config = worker_config.split('/')[1].lower()
210         self.worker_threads = self.get_worker_threads(worker_threads)
211         self.vnf_type = vnf_type
212         self.pipe_line = 0
213         self.vnfd_helper = vnfd_helper
214         self.write_parser = ConfigParser()
215         self.read_parser = ConfigParser()
216         self.read_parser.read(config_tpl)
217         self.master_core = self.read_parser.get("PIPELINE0", "core")
218         self.master_tpl = self.get_config_tpl_data('MASTER')
219         self.arpicmp_tpl = self.get_config_tpl_data('ARPICMP')
220         self.txrx_tpl = self.get_config_tpl_data('TXRX')
221         self.loadb_tpl = self.get_config_tpl_data('LOADB')
222         self.vnf_tpl = self.get_config_tpl_data(vnf_type)
223         self.swq = 0
224         self.lb_count = int(lb_count)
225         self.lb_config = lb_config
226         self.tmp_file = os.path.join("/tmp", tmp_file)
227         self.pktq_out_os = []
228         self.socket = socket
229         self.start_core = 0
230         self.pipeline_counter = ""
231         self.txrx_pipeline = ""
232         self._port_pairs = None
233         self.all_ports = []
234         self.port_pair_list = []
235         self.lb_to_port_pair_mapping = {}
236         self.init_eal()
237
238         self.lb_index = None
239         self.mul = 0
240         self.port_pairs = []
241         self.ports_len = 0
242         self.prv_que_handler = None
243         self.vnfd = None
244         self.rules = None
245         self.pktq_out = []
246
247     @staticmethod
248     def gen_core(core):
249         # return "s{}c{}".format(self.socket, core)
250         # don't use sockets for VNFs, because we don't want to have to
251         # adjust VM CPU topology.  It is virtual anyway
252         return str(core)
253
254     def make_port_pairs_iter(self, operand, iterable):
255         return (operand(self.vnfd_helper.port_num(x), y) for y in iterable for x in
256                 chain.from_iterable(self.port_pairs))
257
258     def make_range_port_pairs_iter(self, operand, start, end):
259         return self.make_port_pairs_iter(operand, range(start, end))
260
261     def init_eal(self):
262         lines = ['[EAL]\n']
263         vpci = (v['virtual-interface']["vpci"] for v in self.vnfd_helper.interfaces)
264         lines.extend('w = {0}\n'.format(item) for item in vpci)
265         lines.append('\n')
266         with open(self.tmp_file, 'w') as fh:
267             fh.writelines(lines)
268
269     def update_timer(self):
270         timer_tpl = self.get_config_tpl_data('TIMER')
271         timer_tpl['core'] = self.gen_core(0)
272         self.update_write_parser(timer_tpl)
273
274     def get_config_tpl_data(self, type_value):
275         for section in self.read_parser.sections():
276             if self.read_parser.has_option(section, 'type'):
277                 if type_value == self.read_parser.get(section, 'type'):
278                     tpl = OrderedDict(self.read_parser.items(section))
279                     return tpl
280
281     def get_txrx_tpl_data(self, value):
282         for section in self.read_parser.sections():
283             if self.read_parser.has_option(section, 'pipeline_txrx_type'):
284                 if value == self.read_parser.get(section, 'pipeline_txrx_type'):
285                     tpl = OrderedDict(self.read_parser.items(section))
286                     return tpl
287
288     def init_write_parser_template(self, type_value='ARPICMP'):
289         for section in self.read_parser.sections():
290             if type_value == self.parser_get(self.read_parser, section, 'type', object()):
291                 self.pipeline_counter = self.read_parser.getint(section, 'core')
292                 self.txrx_pipeline = self.read_parser.getint(section, 'core')
293                 return
294             self.write_parser.add_section(section)
295             for name, value in self.read_parser.items(section):
296                 self.write_parser.set(section, name, value)
297
298     def update_write_parser(self, data):
299         section = "PIPELINE{0}".format(self.pipeline_counter)
300         self.write_parser.add_section(section)
301         for name, value in data.items():
302             self.write_parser.set(section, name, value)
303
304     def get_worker_threads(self, worker_threads):
305         if self.worker_config == '1t':
306             return worker_threads
307         else:
308             return worker_threads - worker_threads % 2
309
310     def generate_next_core_id(self):
311         if self.worker_config == '1t':
312             self.start_core += 1
313             return
314
315         try:
316             self.start_core = '{}h'.format(int(self.start_core))
317         except ValueError:
318             self.start_core = int(self.start_core[:-1]) + 1
319
320     def get_lb_count(self):
321         self.lb_count = int(min(len(self.port_pair_list), self.lb_count))
322
323     def generate_lb_to_port_pair_mapping(self):
324         self.lb_to_port_pair_mapping = defaultdict(int)
325         port_pair_count = len(self.port_pair_list)
326         lb_pair_count = int(port_pair_count / self.lb_count)
327         extra = port_pair_count % self.lb_count
328         extra_iter = repeat(lb_pair_count + 1, extra)
329         norm_iter = repeat(lb_pair_count, port_pair_count - extra)
330         new_values = {i: v for i, v in enumerate(chain(extra_iter, norm_iter), 1)}
331         self.lb_to_port_pair_mapping.update(new_values)
332
333     def set_priv_to_pub_mapping(self):
334         port_nums = [tuple(self.vnfd_helper.port_nums(x)) for x in self.port_pair_list]
335         return "".join(str(y).replace(" ", "") for y in
336                        port_nums)
337
338     def set_priv_que_handler(self):
339         # iterated twice, can't be generator
340         priv_to_pub_map = [tuple(self.vnfd_helper.port_nums(x)) for x in self.port_pairs]
341         # must be list to use .index()
342         port_list = list(chain.from_iterable(priv_to_pub_map))
343         uplink_ports = (x[0] for x in priv_to_pub_map)
344         self.prv_que_handler = '({})'.format(
345             "".join(("{},".format(port_list.index(x)) for x in uplink_ports)))
346
347     def generate_arp_route_tbl(self):
348         arp_route_tbl_tmpl = "({port0_dst_ip_hex},{port0_netmask_hex},{port_num}," \
349                              "{next_hop_ip_hex})"
350
351         def build_arp_config(port):
352             dpdk_port_num = self.vnfd_helper.port_num(port)
353             interface = self.vnfd_helper.find_interface(name=port)["virtual-interface"]
354             # We must use the dst because we are on the VNF and we need to
355             # reach the TG.
356             dst_port0_ip = ipaddress.ip_interface(six.text_type(
357                 "%s/%s" % (interface["dst_ip"], interface["netmask"])))
358
359             arp_vars = {
360                 "port0_dst_ip_hex": ip_to_hex(dst_port0_ip.network.network_address.exploded),
361                 "port0_netmask_hex": ip_to_hex(dst_port0_ip.network.netmask.exploded),
362                 # this is the port num that contains port0 subnet and next_hop_ip_hex
363                 # this is LINKID which should be based on DPDK port number
364                 "port_num": dpdk_port_num,
365                 # next hop is dst in this case
366                 # must be within subnet
367                 "next_hop_ip_hex": ip_to_hex(dst_port0_ip.ip.exploded),
368             }
369             return arp_route_tbl_tmpl.format(**arp_vars)
370
371         return ' '.join(build_arp_config(port) for port in self.all_ports)
372
373     def generate_arpicmp_data(self):
374         swq_in_str = self.make_range_str('SWQ{}', self.swq, offset=self.lb_count)
375         self.swq += self.lb_count
376         swq_out_str = self.make_range_str('SWQ{}', self.swq, offset=self.lb_count)
377         self.swq += self.lb_count
378         # ports_mac_list is disabled for some reason
379
380         # mac_iter = (self.vnfd_helper.find_interface(name=port)['virtual-interface']['local_mac']
381         #             for port in self.all_ports)
382         pktq_in_iter = ('RXQ{}.0'.format(self.vnfd_helper.port_num(x[0])) for x in
383                         self.port_pair_list)
384
385         arpicmp_data = {
386             'core': self.gen_core(0),
387             'pktq_in': swq_in_str,
388             'pktq_out': swq_out_str,
389             # we need to disable ports_mac_list?
390             # it looks like ports_mac_list is no longer required
391             # 'ports_mac_list': ' '.join(mac_iter),
392             'pktq_in_prv': ' '.join(pktq_in_iter),
393             'prv_to_pub_map': self.set_priv_to_pub_mapping(),
394             'arp_route_tbl': self.generate_arp_route_tbl(),
395             # nd_route_tbl must be set or we get segault on random OpenStack IPv6 traffic
396             # 'nd_route_tbl': "(0064:ff9b:0:0:0:0:9810:6414,120,0,0064:ff9b:0:0:0:0:9810:6414)"
397             # safe default?  route discard prefix to localhost
398             'nd_route_tbl': "(0100::,64,0,::1)"
399         }
400         self.pktq_out_os = swq_out_str.split(' ')
401         # HWLB is a run to complition. So override the pktq_in/pktq_out
402         if self.lb_config == self.HW_LB:
403             self.swq = 0
404             swq_in_str = \
405                 self.make_range_str('SWQ{}', self.swq,
406                                     offset=(self.lb_count * self.worker_threads))
407             arpicmp_data['pktq_in'] = swq_in_str
408             # WA: Since port_pairs will not be populated during arp pipeline
409             self.port_pairs = self.port_pair_list
410             port_iter = \
411                 self.make_port_pairs_iter(self.float_x_plus_one_tenth_of_y, [self.mul])
412             pktq_out = self.make_str('TXQ{}', port_iter)
413             arpicmp_data['pktq_out'] = pktq_out
414
415         return arpicmp_data
416
417     def generate_final_txrx_data(self, core=0):
418         swq_start = self.swq - self.ports_len * self.worker_threads
419
420         txq_start = 0
421         txq_end = self.worker_threads
422
423         pktq_out_iter = self.make_range_port_pairs_iter(self.float_x_plus_one_tenth_of_y,
424                                                         txq_start, txq_end)
425
426         swq_str = self.make_range_str('SWQ{}', swq_start, self.swq)
427         txq_str = self.make_str('TXQ{}', pktq_out_iter)
428         rxtx_data = {
429             'pktq_in': swq_str,
430             'pktq_out': txq_str,
431             'pipeline_txrx_type': 'TXTX',
432             'core': self.gen_core(core),
433         }
434         pktq_in = rxtx_data['pktq_in']
435         pktq_in = '{0} {1}'.format(pktq_in, self.pktq_out_os[self.lb_index - 1])
436         rxtx_data['pktq_in'] = pktq_in
437         self.pipeline_counter += 1
438         return rxtx_data
439
440     def generate_initial_txrx_data(self):
441         pktq_iter = self.make_range_port_pairs_iter(self.float_x_plus_one_tenth_of_y,
442                                                     0, self.worker_threads)
443
444         rxq_str = self.make_str('RXQ{}', pktq_iter)
445         swq_str = self.make_range_str('SWQ{}', self.swq, offset=self.ports_len)
446         txrx_data = {
447             'pktq_in': rxq_str,
448             'pktq_out': swq_str + ' SWQ{0}'.format(self.lb_index - 1),
449             'pipeline_txrx_type': 'RXRX',
450             'core': self.gen_core(self.start_core),
451         }
452         self.pipeline_counter += 1
453         return self.start_core, txrx_data
454
455     def generate_lb_data(self):
456         pktq_in = self.make_range_str('SWQ{}', self.swq, offset=self.ports_len)
457         self.swq += self.ports_len
458
459         offset = self.ports_len * self.worker_threads
460         pktq_out = self.make_range_str('SWQ{}', self.swq, offset=offset)
461         self.pktq_out = pktq_out.split()
462
463         self.swq += (self.ports_len * self.worker_threads)
464         lb_data = {
465             'prv_que_handler': self.prv_que_handler,
466             'pktq_in': pktq_in,
467             'pktq_out': pktq_out,
468             'n_vnf_threads': str(self.worker_threads),
469             'core': self.gen_core(self.start_core),
470         }
471         self.pipeline_counter += 1
472         return lb_data
473
474     def generate_vnf_data(self):
475         if self.lb_config == self.HW_LB:
476             port_iter = self.make_port_pairs_iter(self.float_x_plus_one_tenth_of_y, [self.mul])
477             pktq_in = self.make_str('RXQ{}', port_iter)
478
479             self.mul += 1
480             port_iter = self.make_port_pairs_iter(self.float_x_plus_one_tenth_of_y, [self.mul])
481             pktq_out = self.make_str('TXQ{}', port_iter)
482
483             pipe_line_data = {
484                 'pktq_in': pktq_in,
485                 'pktq_out': pktq_out + ' SWQ{0}'.format(self.swq),
486                 'prv_que_handler': self.prv_que_handler,
487                 'core': self.gen_core(self.start_core),
488             }
489             self.swq += 1
490         else:
491             pipe_line_data = {
492                 'pktq_in': ' '.join((self.pktq_out.pop(0) for _ in range(self.ports_len))),
493                 'pktq_out': self.make_range_str('SWQ{}', self.swq, offset=self.ports_len),
494                 'prv_que_handler': self.prv_que_handler,
495                 'core': self.gen_core(self.start_core),
496             }
497             self.swq += self.ports_len
498
499         if self.vnf_type in ('ACL', 'VFW'):
500             pipe_line_data.pop('prv_que_handler')
501
502         if self.vnf_tpl.get('vnf_set'):
503             public_ip_port_range_list = self.vnf_tpl['public_ip_port_range'].split(':')
504             ip_in_hex = '{:x}'.format(int(public_ip_port_range_list[0], 16) + self.lb_index - 1)
505             public_ip_port_range_list[0] = ip_in_hex
506             self.vnf_tpl['public_ip_port_range'] = ':'.join(public_ip_port_range_list)
507
508         self.pipeline_counter += 1
509         return pipe_line_data
510
511     def generate_config_data(self):
512         self.init_write_parser_template()
513
514         # use master core for master, don't use self.start_core
515         self.write_parser.set('PIPELINE0', 'core', self.gen_core(self.master_core))
516         arpicmp_data = self.generate_arpicmp_data()
517         self.arpicmp_tpl.update(arpicmp_data)
518         self.update_write_parser(self.arpicmp_tpl)
519
520         if self.vnf_type == 'CGNAPT':
521             self.pipeline_counter += 1
522             self.update_timer()
523
524         if self.lb_config == 'HW':
525             self.start_core = 1
526
527         for lb in self.lb_to_port_pair_mapping:
528             self.lb_index = lb
529             self.mul = 0
530             port_pair_count = self.lb_to_port_pair_mapping[lb]
531             if not self.port_pair_list:
532                 continue
533
534             self.port_pairs = self.port_pair_list[:port_pair_count]
535             self.port_pair_list = self.port_pair_list[port_pair_count:]
536             self.ports_len = port_pair_count * 2
537             self.set_priv_que_handler()
538             if self.lb_config == 'SW':
539                 core, txrx_data = self.generate_initial_txrx_data()
540                 self.txrx_tpl.update(txrx_data)
541                 self.update_write_parser(self.txrx_tpl)
542                 self.start_core += 1
543                 lb_data = self.generate_lb_data()
544                 self.loadb_tpl.update(lb_data)
545                 self.update_write_parser(self.loadb_tpl)
546                 self.start_core += 1
547
548             for i in range(self.worker_threads):
549                 vnf_data = self.generate_vnf_data()
550                 if not self.vnf_tpl:
551                     self.vnf_tpl = {}
552                 self.vnf_tpl.update(vnf_data)
553                 self.update_write_parser(self.vnf_tpl)
554                 try:
555                     self.vnf_tpl.pop('vnf_set')
556                 except KeyError:
557                     pass
558                 else:
559                     self.vnf_tpl.pop('public_ip_port_range')
560                 self.generate_next_core_id()
561
562             if self.lb_config == 'SW':
563                 txrx_data = self.generate_final_txrx_data(core)
564                 self.txrx_tpl.update(txrx_data)
565                 self.update_write_parser(self.txrx_tpl)
566             self.vnf_tpl = self.get_config_tpl_data(self.vnf_type)
567
568     def generate_config(self):
569         self._port_pairs = PortPairs(self.vnfd_helper.interfaces)
570         self.port_pair_list = self._port_pairs.port_pair_list
571         self.all_ports = self._port_pairs.all_ports
572
573         self.get_lb_count()
574         self.generate_lb_to_port_pair_mapping()
575         self.generate_config_data()
576         self.write_parser.write(sys.stdout)
577         with open(self.tmp_file, 'a') as tfh:
578             self.write_parser.write(tfh)
579
580     def generate_link_config(self):
581         def build_args(port):
582             # lookup interface by name
583             virtual_interface = self.vnfd_helper.find_interface(name=port)["virtual-interface"]
584             local_ip = virtual_interface["local_ip"]
585             netmask = virtual_interface["netmask"]
586             port_num = self.vnfd_helper.port_num(port)
587             port_ip, prefix_len = self.validate_ip_and_prefixlen(local_ip, netmask)
588             return LINK_CONFIG_TEMPLATE.format(port_num, port_ip, prefix_len)
589
590         return ''.join(build_args(port) for port in self.all_ports)
591
592     def get_route_data(self, src_key, data_key, port):
593         route_list = self.vnfd['vdu'][0].get(src_key, [])
594         try:
595             return next((route[data_key] for route in route_list if route['if'] == port), None)
596         except (TypeError, StopIteration, KeyError):
597             return None
598
599     def get_ports_gateway(self, port):
600         return self.get_route_data('routing_table', 'gateway', port)
601
602     def get_ports_gateway6(self, port):
603         return self.get_route_data('nd_route_tbl', 'gateway', port)
604
605     def get_netmask_gateway(self, port):
606         return self.get_route_data('routing_table', 'netmask', port)
607
608     def get_netmask_gateway6(self, port):
609         return self.get_route_data('nd_route_tbl', 'netmask', port)
610
611     def generate_arp_config(self):
612         arp_config = []
613         for port in self.all_ports:
614             # ignore gateway, always use TG IP
615             # gateway = self.get_ports_gateway(port)
616             vintf = self.vnfd_helper.find_interface(name=port)["virtual-interface"]
617             dst_mac = vintf["dst_mac"]
618             dst_ip = vintf["dst_ip"]
619             # arp_config.append(
620             #     (self.vnfd_helper.port_num(port), gateway, dst_mac, self.txrx_pipeline))
621             # so dst_mac is the TG dest mac, so we need TG dest IP.
622             # should be dpdk_port_num
623             arp_config.append(
624                 (self.vnfd_helper.port_num(port), dst_ip, dst_mac, self.txrx_pipeline))
625
626         return '\n'.join(('p {3} arpadd {0} {1} {2}'.format(*values) for values in arp_config))
627
628     def generate_arp_config6(self):
629         arp_config6 = []
630         for port in self.all_ports:
631             # ignore gateway, always use TG IP
632             # gateway6 = self.get_ports_gateway6(port)
633             vintf = self.vnfd_helper.find_interface(name=port)["virtual-interface"]
634             dst_mac6 = vintf["dst_mac"]
635             dst_ip6 = vintf["dst_ip"]
636             # arp_config6.append(
637             #     (self.vnfd_helper.port_num(port), gateway6, dst_mac6, self.txrx_pipeline))
638             arp_config6.append(
639                 (self.vnfd_helper.port_num(port), dst_ip6, dst_mac6, self.txrx_pipeline))
640
641         return '\n'.join(('p {3} arpadd {0} {1} {2}'.format(*values) for values in arp_config6))
642
643     def generate_action_config(self):
644         port_list = (self.vnfd_helper.port_num(p) for p in self.all_ports)
645         if self.vnf_type == "VFW":
646             template = FW_ACTION_TEMPLATE
647         else:
648             template = ACTION_TEMPLATE
649
650         return ''.join((template.format(port) for port in port_list))
651
652     def get_ip_from_port(self, port):
653         # we can't use gateway because in OpenStack gateways interfer with floating ip routing
654         # return self.make_ip_addr(self.get_ports_gateway(port), self.get_netmask_gateway(port))
655         vintf = self.vnfd_helper.find_interface(name=port)["virtual-interface"]
656         ip = vintf["local_ip"]
657         netmask = vintf["netmask"]
658         return self.make_ip_addr(ip, netmask)
659
660     def get_network_and_prefixlen_from_ip_of_port(self, port):
661         ip_addr = self.get_ip_from_port(port)
662         # handle cases with no gateway
663         if ip_addr:
664             return ip_addr.network.network_address.exploded, ip_addr.network.prefixlen
665         else:
666             return None, None
667
668     def generate_rule_config(self):
669         cmd = 'acl' if self.vnf_type == "ACL" else "vfw"
670         rules_config = self.rules if self.rules else ''
671         new_rules = []
672         new_ipv6_rules = []
673         pattern = 'p {0} add {1} {2} {3} {4} {5} 0 65535 0 65535 0 0 {6}'
674         for src_intf, dst_intf in self.port_pair_list:
675             src_port = self.vnfd_helper.port_num(src_intf)
676             dst_port = self.vnfd_helper.port_num(dst_intf)
677
678             src_net, src_prefix_len = self.get_network_and_prefixlen_from_ip_of_port(src_intf)
679             dst_net, dst_prefix_len = self.get_network_and_prefixlen_from_ip_of_port(dst_intf)
680             # ignore entires with empty values
681             if all((src_net, src_prefix_len, dst_net, dst_prefix_len)):
682                 new_rules.append((cmd, self.txrx_pipeline, src_net, src_prefix_len,
683                                   dst_net, dst_prefix_len, dst_port))
684                 new_rules.append((cmd, self.txrx_pipeline, dst_net, dst_prefix_len,
685                                   src_net, src_prefix_len, src_port))
686
687             # src_net = self.get_ports_gateway6(port_pair[0])
688             # src_prefix_len = self.get_netmask_gateway6(port_pair[0])
689             # dst_net = self.get_ports_gateway6(port_pair[1])
690             # dst_prefix_len = self.get_netmask_gateway6(port_pair[0])
691             # # ignore entires with empty values
692             # if all((src_net, src_prefix_len, dst_net, dst_prefix_len)):
693             #     new_ipv6_rules.append((cmd, self.txrx_pipeline, src_net, src_prefix_len,
694             #                            dst_net, dst_prefix_len, dst_port))
695             #     new_ipv6_rules.append((cmd, self.txrx_pipeline, dst_net, dst_prefix_len,
696             #                            src_net, src_prefix_len, src_port))
697
698         acl_apply = "\np %s applyruleset" % cmd
699         new_rules_config = '\n'.join(pattern.format(*values) for values
700                                      in chain(new_rules, new_ipv6_rules))
701         return ''.join([rules_config, new_rules_config, acl_apply])
702
703     def generate_script_data(self):
704         self._port_pairs = PortPairs(self.vnfd_helper.interfaces)
705         self.port_pair_list = self._port_pairs.port_pair_list
706         self.get_lb_count()
707         script_data = {
708             'link_config': self.generate_link_config(),
709             'arp_config': self.generate_arp_config(),
710             # disable IPv6 for now
711             # 'arp_config6': self.generate_arp_config6(),
712             'arp_config6': "",
713             'actions': '',
714             'rules': '',
715         }
716
717         if self.vnf_type in ('ACL', 'VFW'):
718             script_data.update({
719                 'actions': self.generate_action_config(),
720                 'rules': self.generate_rule_config(),
721             })
722
723         return script_data
724
725     def generate_script(self, vnfd, rules=None):
726         self.vnfd = vnfd
727         self.rules = rules
728         script_data = self.generate_script_data()
729         script = SCRIPT_TPL.format(**script_data)
730         if self.lb_config == self.HW_LB:
731             script += 'set fwd rxonly'
732             hwlb_tpl = """
733 set_sym_hash_ena_per_port {0} enable
734 set_hash_global_config {0} simple_xor ipv4-udp enable
735 set_sym_hash_ena_per_port {1} enable
736 set_hash_global_config {1} simple_xor ipv4-udp enable
737 set_hash_input_set {0} ipv4-udp src-ipv4 udp-src-port add
738 set_hash_input_set {1} ipv4-udp dst-ipv4 udp-dst-port add
739 set_hash_input_set {0} ipv6-udp src-ipv6 udp-src-port add
740 set_hash_input_set {1} ipv6-udp dst-ipv6 udp-dst-port add
741 """
742             for port_pair in self.port_pair_list:
743                 script += hwlb_tpl.format(*(self.vnfd_helper.port_nums(port_pair)))
744         return script