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