Merge "Added TC for vFW in heat context with ixia TG"
[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 = defaultdict(list)
233         for private_intf in interfaces:
234             vintf = private_intf['virtual-interface']
235             networks[vintf['vld_id']].append(vintf)
236
237         for name, net in networks.items():
238             # partition returns a tuple
239             parts = list(name.partition('private'))
240             if parts[0]:
241                 # 'private' was not in or not leftmost in the string
242                 continue
243             parts[1] = 'public'
244             public_id = ''.join(parts)
245             for private_intf in net:
246                 try:
247                     public_peer_intfs = networks[public_id]
248                 except KeyError:
249                     LOG.warning("private network without peer %s, %s not found", name, public_id)
250                     continue
251
252                 for public_intf in public_peer_intfs:
253                     port_pair = private_intf["ifname"], public_intf["ifname"]
254                     port_pair_list.append(port_pair)
255
256         return port_pair_list, networks
257
258     def get_lb_count(self):
259         self.lb_count = int(min(len(self.port_pair_list), self.lb_count))
260
261     def generate_lb_to_port_pair_mapping(self):
262         self.lb_to_port_pair_mapping = defaultdict(int)
263         port_pair_count = len(self.port_pair_list)
264         lb_pair_count = int(port_pair_count / self.lb_count)
265         for i in range(self.lb_count):
266             self.lb_to_port_pair_mapping[i + 1] = lb_pair_count
267         for i in range(port_pair_count % self.lb_count):
268             self.lb_to_port_pair_mapping[i + 1] += 1
269
270     def set_priv_to_pub_mapping(self):
271         return "".join(str(y) for y in [(int(x[0][-1]), int(x[1][-1])) for x in
272                                         self.port_pair_list])
273
274     def set_priv_que_handler(self):
275         # iterated twice, can't be generator
276         priv_to_pub_map = [(int(x[0][-1]), int(x[1][-1])) for x in self.port_pairs]
277         # must be list to use .index()
278         port_list = list(chain.from_iterable(priv_to_pub_map))
279         priv_ports = (x[0] for x in priv_to_pub_map)
280         self.prv_que_handler = '({})'.format(
281             ",".join((str(port_list.index(x)) for x in priv_ports)))
282
283     def generate_arp_route_tbl(self):
284         arp_config = []
285         arp_route_tbl_tmpl = "({port0_dst_ip_hex},{port0_netmask_hex},{port_num}," \
286                              "{next_hop_ip_hex})"
287         for port_pair in self.port_pair_list:
288             for port in port_pair:
289                 port_num = int(port[-1])
290                 interface = self.interfaces[port_num]
291                 # We must use the dst because we are on the VNF and we need to
292                 # reach the TG.
293                 dst_port0_ip = \
294                     ipaddress.ip_interface(six.text_type(
295                         "%s/%s" % (interface["virtual-interface"]["dst_ip"],
296                                    interface["virtual-interface"]["netmask"])))
297                 arp_vars = {
298                     "port0_dst_ip_hex": ip_to_hex(dst_port0_ip.network.network_address.exploded),
299                     "port0_netmask_hex": ip_to_hex(dst_port0_ip.network.netmask.exploded),
300                     # this is the port num that contains port0 subnet and next_hop_ip_hex
301                     "port_num": port_num,
302                     # next hop is dst in this case
303                     # must be within subnet
304                     "next_hop_ip_hex": ip_to_hex(dst_port0_ip.ip.exploded),
305                 }
306                 arp_config.append(arp_route_tbl_tmpl.format(**arp_vars))
307
308         return ' '.join(arp_config)
309
310     def generate_arpicmp_data(self):
311         swq_in_str = self.make_range_str('SWQ{}', self.swq, offset=self.lb_count)
312         self.swq += self.lb_count
313         swq_out_str = self.make_range_str('SWQ{}', self.swq, offset=self.lb_count)
314         self.swq += self.lb_count
315         # ports_mac_list is disabled for some reason
316         # mac_iter = (self.interfaces[int(x[-1])]['virtual-interface']['local_mac']
317         #             for port_pair in self.port_pair_list for x in port_pair)
318         pktq_in_iter = ('RXQ{}'.format(float(x[0][-1])) for x in self.port_pair_list)
319
320         arpicmp_data = {
321             'core': self.gen_core(self.start_core),
322             'pktq_in': swq_in_str,
323             'pktq_out': swq_out_str,
324             # we need to disable ports_mac_list?
325             # it looks like ports_mac_list is no longer required
326             # 'ports_mac_list': ' '.join(mac_iter),
327             'pktq_in_prv': ' '.join(pktq_in_iter),
328             'prv_to_pub_map': self.set_priv_to_pub_mapping(),
329             'arp_route_tbl': self.generate_arp_route_tbl(),
330             # nd_route_tbl must be set or we get segault on random OpenStack IPv6 traffic
331             # 'nd_route_tbl': "(0064:ff9b:0:0:0:0:9810:6414,120,0,0064:ff9b:0:0:0:0:9810:6414)"
332             # safe default?  route discard prefix to localhost
333             'nd_route_tbl': "(0100::,64,0,::1)"
334         }
335         self.pktq_out_os = swq_out_str.split(' ')
336         # HWLB is a run to complition. So override the pktq_in/pktq_out
337         if self.lb_config == self.HW_LB:
338             self.swq = 0
339             swq_in_str = \
340                 self.make_range_str('SWQ{}', self.swq,
341                                     offset=(self.lb_count * self.worker_threads))
342             arpicmp_data['pktq_in'] = swq_in_str
343             # WA: Since port_pairs will not be populated during arp pipeline
344             self.port_pairs = self.port_pair_list
345             port_iter = \
346                 self.make_port_pairs_iter(self.float_x_plus_one_tenth_of_y, [self.mul])
347             pktq_out = self.make_str('TXQ{}', port_iter)
348             arpicmp_data['pktq_out'] = pktq_out
349
350         return arpicmp_data
351
352     def generate_final_txrx_data(self):
353         swq_start = self.swq - self.ports_len * self.worker_threads
354
355         txq_start = 0
356         txq_end = self.worker_threads
357
358         pktq_out_iter = self.make_range_port_pairs_iter(self.float_x_plus_one_tenth_of_y,
359                                                         txq_start, txq_end)
360
361         swq_str = self.make_range_str('SWQ{}', swq_start, self.swq)
362         txq_str = self.make_str('TXQ{}', pktq_out_iter)
363         rxtx_data = {
364             'pktq_in': swq_str,
365             'pktq_out': txq_str,
366             'pipeline_txrx_type': 'TXTX',
367             'core': self.gen_core(self.start_core),
368         }
369         pktq_in = rxtx_data['pktq_in']
370         pktq_in = '{0} {1}'.format(pktq_in, self.pktq_out_os[self.lb_index - 1])
371         rxtx_data['pktq_in'] = pktq_in
372         self.pipeline_counter += 1
373         return rxtx_data
374
375     def generate_initial_txrx_data(self):
376         pktq_iter = self.make_range_port_pairs_iter(self.float_x_plus_one_tenth_of_y,
377                                                     0, self.worker_threads)
378
379         rxq_str = self.make_str('RXQ{}', pktq_iter)
380         swq_str = self.make_range_str('SWQ{}', self.swq, offset=self.ports_len)
381         txrx_data = {
382             'pktq_in': rxq_str,
383             'pktq_out': swq_str + ' SWQ{0}'.format(self.lb_index - 1),
384             'pipeline_txrx_type': 'RXRX',
385             'core': self.gen_core(self.start_core),
386         }
387         self.pipeline_counter += 1
388         return txrx_data
389
390     def generate_lb_data(self):
391         pktq_in = self.make_range_str('SWQ{}', self.swq, offset=self.ports_len)
392         self.swq += self.ports_len
393
394         offset = self.ports_len * self.worker_threads
395         pktq_out = self.make_range_str('SWQ{}', self.swq, offset=offset)
396         self.pktq_out = pktq_out.split()
397
398         self.swq += (self.ports_len * self.worker_threads)
399         lb_data = {
400             'prv_que_handler': self.prv_que_handler,
401             'pktq_in': pktq_in,
402             'pktq_out': pktq_out,
403             'n_vnf_threads': str(self.worker_threads),
404             'core': self.gen_core(self.start_core),
405         }
406         self.pipeline_counter += 1
407         return lb_data
408
409     def generate_vnf_data(self):
410         if self.lb_config == self.HW_LB:
411             port_iter = self.make_port_pairs_iter(self.float_x_plus_one_tenth_of_y, [self.mul])
412             pktq_in = self.make_str('RXQ{}', port_iter)
413
414             self.mul += 1
415             port_iter = self.make_port_pairs_iter(self.float_x_plus_one_tenth_of_y, [self.mul])
416             pktq_out = self.make_str('TXQ{}', port_iter)
417
418             pipe_line_data = {
419                 'pktq_in': pktq_in,
420                 'pktq_out': pktq_out + ' SWQ{0}'.format(self.swq),
421                 'prv_que_handler': self.prv_que_handler,
422                 'core': self.gen_core(self.start_core),
423             }
424             self.swq += 1
425         else:
426             pipe_line_data = {
427                 'pktq_in': ' '.join((self.pktq_out.pop(0) for _ in range(self.ports_len))),
428                 'pktq_out': self.make_range_str('SWQ{}', self.swq, offset=self.ports_len),
429                 'prv_que_handler': self.prv_que_handler,
430                 'core': self.gen_core(self.start_core),
431             }
432             self.swq += self.ports_len
433
434         if self.vnf_type in ('ACL', 'VFW'):
435             pipe_line_data.pop('prv_que_handler')
436
437         if self.vnf_tpl.get('vnf_set'):
438             public_ip_port_range_list = self.vnf_tpl['public_ip_port_range'].split(':')
439             ip_in_hex = '{:x}'.format(int(public_ip_port_range_list[0], 16) + self.lb_index - 1)
440             public_ip_port_range_list[0] = ip_in_hex
441             self.vnf_tpl['public_ip_port_range'] = ':'.join(public_ip_port_range_list)
442
443         self.pipeline_counter += 1
444         return pipe_line_data
445
446     def generate_config_data(self):
447         self.init_write_parser_template()
448
449         # use master core for master, don't use self.start_core
450         self.write_parser.set('PIPELINE0', 'core', self.gen_core(self.master_core))
451         arpicmp_data = self.generate_arpicmp_data()
452         self.arpicmp_tpl.update(arpicmp_data)
453         self.update_write_parser(self.arpicmp_tpl)
454
455         self.start_core += 1
456         if self.vnf_type == 'CGNAPT':
457             self.pipeline_counter += 1
458             self.update_timer()
459
460         for lb in self.lb_to_port_pair_mapping:
461             self.lb_index = lb
462             self.mul = 0
463             port_pair_count = self.lb_to_port_pair_mapping[lb]
464             if not self.port_pair_list:
465                 continue
466
467             self.port_pairs = self.port_pair_list[:port_pair_count]
468             self.port_pair_list = self.port_pair_list[port_pair_count:]
469             self.ports_len = port_pair_count * 2
470             self.set_priv_que_handler()
471             if self.lb_config == 'SW':
472                 txrx_data = self.generate_initial_txrx_data()
473                 self.txrx_tpl.update(txrx_data)
474                 self.update_write_parser(self.txrx_tpl)
475                 self.start_core += 1
476                 lb_data = self.generate_lb_data()
477                 self.loadb_tpl.update(lb_data)
478                 self.update_write_parser(self.loadb_tpl)
479                 self.start_core += 1
480
481             for i in range(self.worker_threads):
482                 vnf_data = self.generate_vnf_data()
483                 if not self.vnf_tpl:
484                     self.vnf_tpl = {}
485                 self.vnf_tpl.update(vnf_data)
486                 self.update_write_parser(self.vnf_tpl)
487                 try:
488                     self.vnf_tpl.pop('vnf_set')
489                 except KeyError:
490                     pass
491                 else:
492                     self.vnf_tpl.pop('public_ip_port_range')
493                 self.generate_next_core_id()
494
495             if self.lb_config == 'SW':
496                 txrx_data = self.generate_final_txrx_data()
497                 self.txrx_tpl.update(txrx_data)
498                 self.update_write_parser(self.txrx_tpl)
499                 self.start_core += 1
500             self.vnf_tpl = self.get_config_tpl_data(self.vnf_type)
501
502     def generate_config(self):
503         self.port_pair_list, self.networks = self.get_port_pairs(self.interfaces)
504         self.get_lb_count()
505         self.generate_lb_to_port_pair_mapping()
506         self.generate_config_data()
507         self.write_parser.write(sys.stdout)
508         with open(self.tmp_file, 'a') as tfh:
509             self.write_parser.write(tfh)
510
511     def generate_link_config(self):
512
513         link_configs = []
514         for port_pair in self.port_pair_list:
515             for port in port_pair:
516                 port = port[-1]
517                 virtual_interface = self.interfaces[int(port)]["virtual-interface"]
518                 local_ip = virtual_interface["local_ip"]
519                 netmask = virtual_interface["netmask"]
520                 port_ip, prefix_len = self.validate_ip_and_prefixlen(local_ip, netmask)
521                 link_configs.append(LINK_CONFIG_TEMPLATE.format(port, port_ip, prefix_len))
522
523         return ''.join(link_configs)
524
525     def get_route_data(self, src_key, data_key, port):
526         route_list = self.vnfd['vdu'][0].get(src_key, [])
527         try:
528             return next((route[data_key] for route in route_list if route['if'] == port), None)
529         except (TypeError, StopIteration, KeyError):
530             return None
531
532     def get_ports_gateway(self, port):
533         return self.get_route_data('routing_table', 'gateway', port)
534
535     def get_ports_gateway6(self, port):
536         return self.get_route_data('nd_route_tbl', 'gateway', port)
537
538     def get_netmask_gateway(self, port):
539         return self.get_route_data('routing_table', 'netmask', port)
540
541     def get_netmask_gateway6(self, port):
542         return self.get_route_data('nd_route_tbl', 'netmask', port)
543
544     def generate_arp_config(self):
545         arp_config = []
546         for port_pair in self.port_pair_list:
547             for port in port_pair:
548                 # ignore gateway, always use TG IP
549                 # gateway = self.get_ports_gateway(port)
550                 dst_mac = self.interfaces[int(port[-1])]["virtual-interface"]["dst_mac"]
551                 dst_ip = self.interfaces[int(port[-1])]["virtual-interface"]["dst_ip"]
552                 # arp_config.append((port[-1], gateway, dst_mac, self.txrx_pipeline))
553                 # so dst_mac is the TG dest mac, so we need TG dest IP.
554                 arp_config.append((port[-1], dst_ip, dst_mac, self.txrx_pipeline))
555
556         return '\n'.join(('p {3} arpadd {0} {1} {2}'.format(*values) for values in arp_config))
557
558     def generate_arp_config6(self):
559         arp_config6 = []
560         for port_pair in self.port_pair_list:
561             for port in port_pair:
562                 # ignore gateway, always use TG IP
563                 # gateway6 = self.get_ports_gateway6(port)
564                 dst_mac6 = self.interfaces[int(port[-1])]["virtual-interface"]["dst_mac"]
565                 dst_ip6 = self.interfaces[int(port[-1])]["virtual-interface"]["dst_ip"]
566                 # arp_config6.append((port[-1], gateway6, dst_mac6, self.txrx_pipeline))
567                 arp_config6.append((port[-1], dst_ip6, dst_mac6, self.txrx_pipeline))
568
569         return '\n'.join(('p {3} arpadd {0} {1} {2}'.format(*values) for values in arp_config6))
570
571     def generate_action_config(self):
572         port_list = []
573         for port_pair in self.port_pair_list:
574             for port in port_pair:
575                 port_list.append(port[-1])
576
577         if self.vnf_type == "VFW":
578             template = FW_ACTION_TEMPLATE
579         else:
580             template = ACTION_TEMPLATE
581
582         return ''.join((template.format(port) for port in port_list))
583
584     def get_ip_from_port(self, port):
585         # we can't use gateway because in OpenStack gateways interfer with floating ip routing
586         # return self.make_ip_addr(self.get_ports_gateway(port), self.get_netmask_gateway(port))
587         ip = self.interfaces[port]["virtual-interface"]["local_ip"]
588         netmask = self.interfaces[port]["virtual-interface"]["netmask"]
589         return self.make_ip_addr(ip, netmask)
590
591     def get_network_and_prefixlen_from_ip_of_port(self, port):
592         ip_addr = self.get_ip_from_port(port)
593         # handle cases with no gateway
594         if ip_addr:
595             return ip_addr.network.network_address.exploded, ip_addr.network.prefixlen
596         else:
597             return None, None
598
599     def generate_rule_config(self):
600         cmd = 'acl' if self.vnf_type == "ACL" else "vfw"
601         rules_config = self.rules if self.rules else ''
602         new_rules = []
603         new_ipv6_rules = []
604         pattern = 'p {0} add {1} {2} {3} {4} {5} 0 65535 0 65535 0 0 {6}'
605         for port_pair in self.port_pair_list:
606             src_port = int(port_pair[0][-1])
607             dst_port = int(port_pair[1][-1])
608
609             src_net, src_prefix_len = self.get_network_and_prefixlen_from_ip_of_port(src_port)
610             dst_net, dst_prefix_len = self.get_network_and_prefixlen_from_ip_of_port(dst_port)
611             # ignore entires with empty values
612             if all((src_net, src_prefix_len, dst_net, dst_prefix_len)):
613                 new_rules.append((cmd, self.txrx_pipeline, src_net, src_prefix_len,
614                                   dst_net, dst_prefix_len, dst_port))
615                 new_rules.append((cmd, self.txrx_pipeline, dst_net, dst_prefix_len,
616                                   src_net, src_prefix_len, src_port))
617
618             # src_net = self.get_ports_gateway6(port_pair[0])
619             # src_prefix_len = self.get_netmask_gateway6(port_pair[0])
620             # dst_net = self.get_ports_gateway6(port_pair[1])
621             # dst_prefix_len = self.get_netmask_gateway6(port_pair[0])
622             # # ignore entires with empty values
623             # if all((src_net, src_prefix_len, dst_net, dst_prefix_len)):
624             #     new_ipv6_rules.append((cmd, self.txrx_pipeline, src_net, src_prefix_len,
625             #                            dst_net, dst_prefix_len, dst_port))
626             #     new_ipv6_rules.append((cmd, self.txrx_pipeline, dst_net, dst_prefix_len,
627             #                            src_net, src_prefix_len, src_port))
628
629         acl_apply = "\np %s applyruleset" % cmd
630         new_rules_config = '\n'.join(pattern.format(*values) for values
631                                      in chain(new_rules, new_ipv6_rules))
632         return ''.join([rules_config, new_rules_config, acl_apply])
633
634     def generate_script_data(self):
635         self.port_pair_list, self.networks = self.get_port_pairs(self.interfaces)
636         self.get_lb_count()
637         script_data = {
638             'link_config': self.generate_link_config(),
639             'arp_config': self.generate_arp_config(),
640             # disable IPv6 for now
641             # 'arp_config6': self.generate_arp_config6(),
642             'arp_config6': "",
643             'actions': '',
644             'rules': '',
645         }
646
647         if self.vnf_type in ('ACL', 'VFW'):
648             script_data.update({
649                 'actions': self.generate_action_config(),
650                 'rules': self.generate_rule_config(),
651             })
652
653         return script_data
654
655     def generate_script(self, vnfd, rules=None):
656         self.vnfd = vnfd
657         self.rules = rules
658         script_data = self.generate_script_data()
659         script = SCRIPT_TPL.format(**script_data)
660         if self.lb_config == self.HW_LB:
661             script += 'set fwd rxonly'
662             hwlb_tpl = """
663 set_sym_hash_ena_per_port {0} enable
664 set_hash_global_config {0} simple_xor ipv4-udp enable
665 set_sym_hash_ena_per_port {1} enable
666 set_hash_global_config {1} simple_xor ipv4-udp enable
667 set_hash_input_set {0} ipv4-udp src-ipv4 udp-src-port add
668 set_hash_input_set {1} ipv4-udp dst-ipv4 udp-dst-port add
669 set_hash_input_set {0} ipv6-udp src-ipv6 udp-src-port add
670 set_hash_input_set {1} ipv6-udp dst-ipv6 udp-dst-port add
671 """
672             for port_pair in self.port_pair_list:
673                 script += hwlb_tpl.format(port_pair[0][-1], port_pair[1][-1])
674         return script